title: how to remove an array element correctly in a for loop
body: <p>NOTE: a permanent copy of this text is available on <a href="https://bpaste.net/raw/20a08beae676" rel="nofollow noreferrer">https://bpaste.net/raw/20a08beae676</a></p>
  <p>this is intended as a self-answer tutorial on how to correctly remove a element from an array in a for loop (in this case i will assume u use a for i in array style for loop)</p>
  <p>NOTE: this is intended for when u want to add or remove an element from a array that the for loop is using, for example, &quot;for i in ${!apples[@]}&quot; ; do blah ; done&quot; if i blindly modify apples the variable i will not be modified accordinly and if u try to change i it will not work as this style of for loop does not permit doing so, this is a solution for that problem</p>
  <p>for this we will use an edited version of <a href="https://stackoverflow.com/a/17533525/8680581">https://stackoverflow.com/a/17533525/8680581</a></p>
  <h3>important</h3>
  <p>the first thing u will want to do is change the for loop into a C style equivilant that will enable you to do the job:</p>
  <pre><code>for (( i=0; i&lt;${#li[*]}; i++ ));
  </code></pre>
  <p>this is equivilant to:</p>
  <pre><code>for i in ${!li[*]};
  </code></pre>
  <h1>function:</h1>
  <pre><code>remove_array() {
  if [[ $1 == &quot;-v&quot; ]]
      then
          verbose=1
          shift 1
  fi
  wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=$1
  shift 1
  wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_indexes=($@)
  if [[ ! -z $verbose ]]
      then
          echo &quot;old array is $(eval &quot;declare -p $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array&quot;)&quot;
  fi
  for i in ${wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_indexes[@]}
      do
          if [[ ! -z $verbose ]]
              then
                  echo &quot;unsetting index $i&quot;
          fi
          eval &quot;unset $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[i]&quot;
  done
  eval &quot;$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=(\&quot;\${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}\&quot;)&quot;
  if [[ ! -z $verbose ]]
      then
          echo &quot;new array is $(eval &quot;declare -p $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array&quot;)&quot;
  fi
  if [[ ! -z $verbose ]]
      then
          unset verbose
  fi
  unset wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_indexes
  }
  </code></pre>
  <p>for those who think this function is overcomplicated, this is what it looks like with the variables non random and the verbose stuff cut out of it:</p>
  <h1>function (non randomized and no verbose option):</h1>
  <pre><code>remove_array() {
  a_array=$1
  shift 1
  a_indexes=($@)
  for i in ${a_indexes[@]}
      do
          eval &quot;unset $a_array[i]&quot;
  done
  eval &quot;$a_array=(\&quot;\${$a_array[@]}\&quot;)&quot;
  unset a_array a_indexes
  }
  </code></pre>
  <h1>code:</h1>
  <pre><code>li=(pi go dl og wa)
  for (( i=0; i&lt;${#li[*]}; i++ ));
      do
          echo &quot;i before: $i&quot;
          echo &quot;is array \&quot;li\&quot; index \&quot;$i\&quot; contents \&quot;${li[i]}\&quot; equal to \&quot;go\&quot; or \&quot;og\&quot;?&quot;
          if [[ &quot;${li[i]}&quot; == &quot;go&quot;  || &quot;${li[i]}&quot; == &quot;og&quot; ]]
              then
              remove_array -v li $i
              i=$(($i-1))
              echo &quot;setting i to $i here should cause i to repeat $i and check for \&quot;go\&quot; or \&quot;og\&quot; again&quot;
          fi
          echo &quot;i after: $i&quot;
  done
  </code></pre>
  <h1>output:</h1>
  <pre><code>i before: 0
  is array &quot;li&quot; index &quot;0&quot; contents &quot;pi&quot; equal to &quot;go&quot; or &quot;og&quot;?
  i after: 0
  i before: 1
  is array &quot;li&quot; index &quot;1&quot; contents &quot;go&quot; equal to &quot;go&quot; or &quot;og&quot;?
  old array is declare -a li=([0]=&quot;pi&quot; [1]=&quot;go&quot; [2]=&quot;dl&quot; [3]=&quot;og&quot; [4]=&quot;wa&quot;)
  unsetting index 1
  new array is declare -a li=([0]=&quot;pi&quot; [1]=&quot;dl&quot; [2]=&quot;og&quot; [3]=&quot;wa&quot;)
  setting i to 0 here should cause i to repeat 0 and check for &quot;go&quot; or &quot;og&quot; again
  i after: 0
  i before: 1
  is array &quot;li&quot; index &quot;1&quot; contents &quot;dl&quot; equal to &quot;go&quot; or &quot;og&quot;?
  i after: 1
  i before: 2
  is array &quot;li&quot; index &quot;2&quot; contents &quot;og&quot; equal to &quot;go&quot; or &quot;og&quot;?
  old array is declare -a li=([0]=&quot;pi&quot; [1]=&quot;dl&quot; [2]=&quot;og&quot; [3]=&quot;wa&quot;)
  unsetting index 2
  new array is declare -a li=([0]=&quot;pi&quot; [1]=&quot;dl&quot; [2]=&quot;wa&quot;)
  setting i to 1 here should cause i to repeat 1 and check for &quot;go&quot; or &quot;og&quot; again
  i after: 1
  i before: 2
  is array &quot;li&quot; index &quot;2&quot; contents &quot;wa&quot; equal to &quot;go&quot; or &quot;og&quot;?
  i after: 2
  </code></pre>
  <h1>in depth overview</h1>
  <h3>the code</h3>
  <p>first we initiate the loop</p>
  <pre><code>for (( i=0; i&lt;${#li[*]}; i++ ));
      do
  </code></pre>
  <p>then we set a condition upon the sub code will activate</p>
  <pre><code>        if [[ &quot;${li[i]}&quot; == &quot;go&quot;  || &quot;${li[i]}&quot; == &quot;og&quot; ]]
  </code></pre>
  <p>this will activate if the contents of array &quot;li&quot; index $i is equal to &quot;go&quot; or &quot;og&quot;</p>
  <p>next we do something, in this case we remove &quot;go&quot; or &quot;og&quot; from the array &quot;li&quot; as so it does not get endlessly triggered when i is decreased</p>
  <pre><code>            then
                  remove_array -v li $i
  </code></pre>
  <p>remove_array -v li $i will remove the index (or indexes if multiple are specified) $i (as in the value of the variable i, not &quot;$i&quot; literally) from the array &quot;li&quot; then it will shrink the array accordingly instead of leaving the index empty which is often undesired</p>
  <p>then once that is done we decrease $i in order to account for the decrease in size of the array otherwise it will just skip array elements as if the array had not decreazed at all (which is bad)</p>
  <pre><code>                i=$(($i-1))
  </code></pre>
  <p>this sets i to the value of i minus 1, for example</p>
  <pre><code>                i = 3
  </code></pre>
  <p>expanded and variable substitution taken into account it will look like this</p>
  <pre><code>                i=$((3-1))
                  &gt;
                  i=$(3 minus 1) # note &quot;minus&quot; is not a real command nor valid syntax
                  &gt;
                  3 - 1 = 2
                  &gt;
                  i=2
  </code></pre>
  <p>after that we end the loop</p>
  <pre><code>        fi
  done
  </code></pre>
  <p>when i is decreased it will take effect in the for loop &quot;i++&quot;</p>
  <pre><code>2++
  &gt;
  3
  &gt;
  i-1
  &gt;
  i=2
  &gt;
  2++
  </code></pre>
  <h3>the function</h3>
  <p>first we set up a small verbose flag for verbose output if we need it, and we check if positional argument 1 is equal to</p>
  <pre><code>-v
  </code></pre>
  <p>of the function (note positional arguments are as below: &lt;&gt; denotes non literal hints but intended as to hint what it CAN be according to what is accaptable)</p>
  <pre><code>(&lt;function or command&gt; arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 arg9 arg10 arg11 and so on for as many characters as ur terminal will allow in a single line)
  
  
  if [[ $1 == &quot;-v&quot; ]]
      then
  </code></pre>
  <p>if true we will set a variable</p>
  <pre><code>        verbose=1
  </code></pre>
  <p>then we shift the positional arguments by 1, making arg1 become arg0, arg2 become arg1, arg3 become arg2 and so on</p>
  <pre><code>        shift 1
  </code></pre>
  <p>and finish the setup</p>
  <pre><code>fi
  </code></pre>
  <p>then we semi-randomize the &quot;array&quot; variable and &quot;index&quot; array, then we set <em>_array (</em> denotes the long semi-random string) to be positional argument 1 of the function</p>
  <pre><code>wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=$1
  </code></pre>
  <p>note since it is unlikely that an array name will contain characters that needs quoting we will not quote positional argument 1</p>
  <p>then we shift the positional arguments by 1</p>
  <pre><code>shift 1
  </code></pre>
  <p>then we set *_index to all of the positional arguments to allow for specifying of multiple indexes, and we make it an array</p>
  <pre><code>wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_indexes=($@)
  </code></pre>
  <p>then we test if $verbose exists</p>
  <pre><code>if [[ ! -z $verbose ]]
  </code></pre>
  <p>and if so, print something</p>
  <pre><code>    then
          echo &quot;old array is $(eval &quot;declare -p $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array&quot;)&quot;
  fi
  </code></pre>
  <p>notice how we use eval, eval can be used to execute commands that would otherwise be impossible to execute due to the command itself being literally dependant on a variable of some sort, note eval works (from my understanding) by first preforming a ecko/printf then executing the result (much like</p>
  <pre><code>echo &quot;ls /&quot; | bash -
  </code></pre>
  <p>only the it is not executed in a subshell and thus has the advantage of veing able to work with the script and has alot of possibilities for its use case, for example u might use it to dynamically generate an if statement that is dependant on an array as to how many if/elif statements there will be which saves alot of time especially if it needs to be done for lots of functions and the array contents is unknown (as you would literally need to change every if statement every time u want to add or remove a needed/uneeded variable, such as a title in a list of movies u watched, or a list of websites to check in a complex order that can easily end up being thousands of lines of code total just to check them all in every function, reduced to just a few lines of code template with eval)</p>
  <pre><code>eval &quot;declare -p $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array&quot;
  </code></pre>
  <p>in this eval, it evaluates the following, assuming *_array contains &quot;array_mine&quot;</p>
  <pre><code>eval &quot;declare -p $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array&quot;
  &gt;
  eval &quot;declare -p array_mine&quot;
  &gt;
  declare -p array_mine
  </code></pre>
  <p>then it executes declare -p array_mine</p>
  <p>next we loop over the array of indexes and unset each index</p>
  <pre><code>for i in ${wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_indexes[@]}
      do
          if [[ ! -z $verbose ]]
              then
                  echo &quot;unsetting index $i&quot;
          fi
          eval &quot;unset $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[i]&quot;
  done
  </code></pre>
  <p>in this eval it is different but still the same concept, again we will assume *_array is array_mine</p>
  <pre><code> eval &quot;unset $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[i]&quot;
  &gt;
  eval &quot;unset array_mine[i]&quot;
  &gt;
  unset array_mine[i]
  </code></pre>
  <p>note that since &quot;i&quot; itself was not mentioned as a variabe explicitely eval does not evaluate it, but it would if it is eval &quot;unset array_mine[$i]&quot; however is useless since the for loop evaluates i instead as it is a paramater of the index value and is directly a number</p>
  <p>next finish the loop</p>
  <pre><code>done
  </code></pre>
  <p>then we shrink the array, this is relatively easy to do</p>
  <pre><code>eval &quot;$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=(\&quot;\${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}\&quot;)&quot;
  </code></pre>
  <p>substitution in place:</p>
  <pre><code>eval &quot;$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=(\&quot;\${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}\&quot;)&quot;
  &gt;
  eval &quot;array_mine=(\&quot;\${array_mine[@]}\&quot;)&quot; # notice how we backslash the quotes and the array, this is done to prevent them from being evaluated or unquoting the quote thus making anything after the quote be interperated as literal command
  &gt;
  array_mine=(&quot;${array_mine[@]}&quot;)
  </code></pre>
  <p>and in case you are wondering here is what would happen if the \ had not have been added</p>
  <pre><code>$ &lt;eval or echo or printf&gt; &quot;$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=(&quot;${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}&quot;)&quot;
  bash: &quot;$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=(&quot;${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}&quot;)&quot;: bad substitution
  $ &lt;eval or echo or printf&gt; &quot;$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=(\&quot;${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}\&quot;)&quot;
  bash: $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=(&quot;${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}&quot;): bad substitution
  $ &lt;eval or echo or printf&gt; &quot;$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=(&quot;\${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}&quot;)&quot;
  li=(${li[@]})
  </code></pre>
  <p>notice how on the last one it prints it with no quoting but seems to print it even though &quot;...&quot;...&quot;...&quot; should be unquoting then requoting</p>
  <p>then to finish up we print the last of out verbose output then unset our variables and end the function, by now im sure you all know how eval works (at least for simple stuff, as eval can be used in very complex ways)</p>
  <pre><code>if [[ ! -z $verbose ]]
      then
          echo &quot;new array is $(eval &quot;declare -p $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array&quot;)&quot;
  fi
  if [[ ! -z $verbose ]]
      then
          unset verbose
  fi
  unset wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_indexes
  }
  </code></pre>
  <h3>sample output of the function remove_array (of both verbose and non verbose):</h3>
  <pre><code>$ array=(1 2 3 4 5 6 8 9 10 7)
  $ remove_array -v array 5 2
  old array is
  declare -a array=([0]=&quot;1&quot; [1]=&quot;2&quot; [2]=&quot;3&quot; [3]=&quot;4&quot; [4]=&quot;5&quot; [5]=&quot;6&quot; [6]=&quot;8&quot; [7]=&quot;9&quot; [8]=&quot;10&quot; [9]=&quot;7&quot;)
  unsetting index 5
  unsetting index 2
  new array is
  declare -a array=([0]=&quot;1&quot; [1]=&quot;2&quot; [2]=&quot;4&quot; [3]=&quot;5&quot; [4]=&quot;8&quot; [5]=&quot;9&quot; [6]=&quot;10&quot; [7]=&quot;7&quot;)
  $ declare -p array
  declare -a array=([0]=&quot;1&quot; [1]=&quot;2&quot; [2]=&quot;4&quot; [3]=&quot;5&quot; [4]=&quot;8&quot; [5]=&quot;9&quot; [6]=&quot;10&quot; [7]=&quot;7&quot;)
  $ array=(1 2 3 4 5 6 8 9 10 7)
  $ remove_array array 1 8 3
  $ declare -p array
  declare -a array=([0]=&quot;1&quot; [1]=&quot;3&quot; [2]=&quot;5&quot; [3]=&quot;6&quot; [4]=&quot;8&quot; [5]=&quot;9&quot; [6]=&quot;7&quot;)
  $ 
  </code></pre>
  <h3>note this CAN be used recursively (such as removing every second index, eg</h3>
  <pre><code>$ array=(1 2 3 4 5 6 8 9 10 7)
  $ remove_array -v array 2     
  old array is
  declare -a array=([0]=&quot;1&quot; [1]=&quot;2&quot; [2]=&quot;3&quot; [3]=&quot;4&quot; [4]=&quot;5&quot; [5]=&quot;6&quot; [6]=&quot;8&quot; [7]=&quot;9&quot; [8]=&quot;10&quot; [9]=&quot;7&quot;)
  unsetting index 2
  new array is
  declare -a array=([0]=&quot;1&quot; [1]=&quot;2&quot; [2]=&quot;4&quot; [3]=&quot;5&quot; [4]=&quot;6&quot; [5]=&quot;8&quot; [6]=&quot;9&quot; [7]=&quot;10&quot; [8]=&quot;7&quot;)
  $ remove_array -v array 4
  old array is
  declare -a array=([0]=&quot;1&quot; [1]=&quot;2&quot; [2]=&quot;4&quot; [3]=&quot;5&quot; [4]=&quot;6&quot; [5]=&quot;8&quot; [6]=&quot;9&quot; [7]=&quot;10&quot; [8]=&quot;7&quot;)
  unsetting index 4
  new array is
  declare -a array=([0]=&quot;1&quot; [1]=&quot;2&quot; [2]=&quot;4&quot; [3]=&quot;5&quot; [4]=&quot;8&quot; [5]=&quot;9&quot; [6]=&quot;10&quot; [7]=&quot;7&quot;)
  $ remove_array -v array 6
  old array is
  declare -a array=([0]=&quot;1&quot; [1]=&quot;2&quot; [2]=&quot;4&quot; [3]=&quot;5&quot; [4]=&quot;8&quot; [5]=&quot;9&quot; [6]=&quot;10&quot; [7]=&quot;7&quot;)
  unsetting index 6
  new array is
  declare -a array=([0]=&quot;1&quot; [1]=&quot;2&quot; [2]=&quot;4&quot; [3]=&quot;5&quot; [4]=&quot;8&quot; [5]=&quot;9&quot; [6]=&quot;7&quot;)
  $ 
  </code></pre>
  <p>and so on)</p>
  <p>(although it is dangous to do so as wanted array indexes may be removed)</p>
  <h3>also works on arrays with spaces</h3>
  <pre><code>$ array=(&quot;1 2&quot; &quot;3 4&quot; &quot;5 6&quot; &quot;8 9&quot; &quot;10 7&quot; 8 9 7 3)
  $ remove_array -v array 3 7
  old array is
  declare -a array=([0]=&quot;1 2&quot; [1]=&quot;3 4&quot; [2]=&quot;5 6&quot; [3]=&quot;8 9&quot; [4]=&quot;10 7&quot; [5]=&quot;8&quot; [6]=&quot;9&quot; [7]=&quot;7&quot; [8]=&quot;3&quot;)
  unsetting index 3
  unsetting index 7
  new array is
  declare -a array=([0]=&quot;1 2&quot; [1]=&quot;3 4&quot; [2]=&quot;5 6&quot; [3]=&quot;10 7&quot; [4]=&quot;8&quot; [5]=&quot;9&quot; [6]=&quot;3&quot;)
  $ 
  </code></pre>
  <p>as a side note here is two eval statement that evaluates some printed code</p>
  <pre><code>eval &quot;$(echo ls /)&quot;
  
  test_func() {
  directories=( \
  bin \
  lib \
  usr \
  proc \
  )
  printf &quot;dirs=(
  /tmp
  /dev
  &quot;
  for i in ${!directories[@]}
      do
  printf '%b' &quot;
  /${directories[i]}
  &quot;
  done
  printf &quot;)
  &quot;
  printf 'dirs_filtered() {
  for i in ${!dirs[@]}
  do
      if [[ ${dirs[i]} =~ &quot;/tm&quot; ]]
          then
              printf &quot;${dirs[i]} detected
  &quot;
      elif [[ ${dirs[i]} =~ &quot;/proc&quot; ]]
          then
              printf &quot;${dirs[i]} detected and is a special file system
  &quot;
      fi
  done
  }
  dirs_filtered
  '
  }
  eval &quot;$(test_func)&quot;
  </code></pre>