76

unset array[0] removes the element but still if I do echo ${array[0]} I get a null value moreover there are other ways of doing this but if an element of an array contains spaces like below

array[0]='james young'
array[1]='mary'
array[2]='randy orton'

but these also fail to do the job

array=${array[@]:1} #removed the 1st element

now I want the new array to be like

array[0]='mary'
array[1]='randy orton'

The spaces cause the trouble after assignment and the actual array becomes like with substitution.

array=(mary randy orton)
vonbrand
  • 18,253
munish
  • 7,987

4 Answers4

100

Just use array syntax on the assignment and quote your variable:

array=("${array[@]:1}") #removed the 1st element

Edit according to question in comment. For $@ you can use it like this:

set -- "${@:2}" #removed the 1st parameter
manatwork
  • 31,277
  • 8
    Note that it doesn't remove the 1st element but the element of indice 0 and reassigns the indices. If the first element was on indice 12, it won't remove anything but will reassign the indices so that what once was on indice 12 will now be on indice 0. It's probably not a concern in the OP's case but should probably be noted for future reference. The behaviour is different with zsh whose arrays are not sparse contrary to ksh or bash. – Stéphane Chazelas Mar 18 '13 at 13:05
  • +1 i guess i am still wrong then if it does not remove the element.I was under the impression that array=("${array[@]:1}") removes the 1st element. – munish Mar 19 '13 at 06:08
  • 4
    Hi @StephaneChazelas. The singular of "indices" is "index". Thanks for your comment! – Steven Lu Aug 10 '13 at 23:09
  • @manatwork How does it work on $@ (for functions and scripts)? – Antoine Sep 08 '16 at 09:34
  • @Antoine, edited the answer. – manatwork Sep 08 '16 at 09:50
  • 3
    @manatwork - re: your edit - why not using shift ? – don_crissti Sep 08 '16 at 09:53
  • 1
    @don_crissti, good point. I focused on the indexing difference and not thought further. Also had in mind the situation when you need to discard variable amount of items, for example to keep exactly the last 3: array=("${array[@]: -3}") and set -- "${@: -3}". So stuck at indices. – manatwork Sep 08 '16 at 10:16
  • 1
    shift $[$#-3] for the last 3 is probably much faster for $@ – Tino Dec 11 '16 at 10:21
  • shift will also remove the first element from $@. – Kusalananda Apr 03 '18 at 11:14
1

This had me thinking. The problem with sed/awk/tail is that they're line by line. After you delete the first line you have to write every other line from pattern space to the file.

  • You can use the following commands to do what you want in seconds.
  • This will write the entire file to an array.
  • Remove the first line as it dumps it back into the file.

    readarray -t aLargeFile < <(cat largefile)
    echo "${aLargeFile[@]:1}" >largefile
    

Just change the largefile to the name of your file.

techraf
  • 5,941
jgshawkey
  • 1,639
  • Why not use sed -i 1d largefile instead? This even works for files bigger than RAM+swap – Tino Dec 11 '16 at 10:15
0

To remove an element at particular index, we can use unset and then do copy to another array. Only just unset is not required in this case. Because unset does not remove the element it just sets null string to the particular index in array.

declare -a arr=('aa' 'bb' 'cc' 'dd' 'ee')
unset 'arr[1]'
declare -a arr2=()
i=0
for element in ${arr[@]}
do
    arr2[$i]=$element
    ((++i))
done
echo ${arr[@]}
echo "1st val is ${arr[1]}, 2nd val is ${arr[2]}"
echo ${arr2[@]}
echo "1st val is ${arr2[1]}, 2nd val is ${arr2[2]}"

Output is

aa cc dd ee
1st val is , 2nd val is cc
aa cc dd ee
1st val is cc, 2nd val is dd
rashok
  • 101
  • NO it does not replace element with a null, or empty string. it removes the element and creates a sparse array. That is an array with 'holes' in it. However looking up ANY undefined element in a sparse array returns a empty string! Sparse Arrays is perfectly valid for bash arrays! You can see it was removed using declare -p arr You can test if it is defined using [[ -v arr[1] ]] || echo not-defined – anthony Jun 19 '20 at 11:35
-2
#!/bin/bash

q=( one two three four five )

echo -e "
  (remove) { [:range:] } <- [:list:]
                | [:range:] => return list with range removed range is in the form of [:digit:]-[:digit:]
"

function remove {
  if [[ $1 =~ ([[:digit:]])(-([[:digit:]]))?   ]]; then
    from=${BASH_REMATCH[1]}
    to=${BASH_REMATCH[3]}
  else
    echo bad range
  fi;shift
  array=( ${@} )
  local start=${array[@]::${from}}
  local rest
  [ -n "$to" ] && rest=${array[@]:((${to}+1))}  || rest=${array[@]:((${from}+1))}
  echo ${start[@]} ${rest[@]}
}

q=( `remove 1 ${q[*]}` )
echo ${q[@]}
~                                                                                                                                                              
~                       
Tegra Detra
  • 5,016
  • 5
    This would be much better if there was something to explain how it works and not just a blob of code. And what's with the tildes at the bottom? – user Oct 08 '13 at 20:16
  • 4
    Seriously, you are correct. This does look like it was written by a hooligan, but thank you. I really only get to sneak this in between hamburger serving days. – Tegra Detra Oct 09 '13 at 06:54
  • If any element of q has spaces in it, this will break it up into multiple elements. – William Everett Aug 21 '14 at 20:40
  • 1
    Not helpful when an author agrees their answer syntax ought to be cleaned-up, and then fails to do so. – MrPotatoHead Jan 04 '24 at 14:49
  • No hope do your own homework, also there are known bugs lurking about this place and this code is best done in C and compiled. Also if you are wandering about this area of programming I suggest rolling your own Regex engine. – Tegra Detra Jan 05 '24 at 22:07