how to shift array value in bash
Solution 1
A general remark. It does not make sense to define an array like this:
folder_mount_point_list="sdb sdc sdd sde sdf sdg"
folderArray=( $folder_mount_point_list )
You would do this instead:
folderArray=(sdb sdc sdd sde sdf sdg)
Now to your question:
set -- sdb sdc sdd sde sdf sdg
for folder_name; do
mkdir "/data/$folder_name"
done
or
set -- sdb sdc sdd sde sdf sdg
while [ $# -gt 0 ]; do
mkdir "/data/$1"
shift
done
Solution 2
To answer the question in the title, you can "shift" an array with the substring/subarray notation. shift
itself works with just the positional parameters.
$ a=(a b c d e)
$ a=("${a[@]:1}")
$ echo "${a[@]}"
b c d e
Similarly, to 'pop' the last item off the array: a=("${a[@]:0:${#a[@]} - 1}" )
or unset "a[${#a[@]}-1]"
So if you wanted to, you could do this:
a=(foo bar doo)
b=(123 456 789)
while [ "${#a[@]}" -gt 0 ]; do
echo "$a $b"
a=("${a[@]:1}")
b=("${b[@]:1}")
done
But it trashes the arrays, and the "shifting" assignments probably copy the data around unnecessarily, so just indexing as usual might be better.
a=(foo bar doo)
b=(123 456 789)
i=0
while [ "$i" -lt "${#a[@]}" ]; do
echo "${a[i]} ${b[i]}"
i=$((i+1))
done
Or maybe use an associative array instead, if you don't care about the order of the items. "${!arr[@]}"
gives the keys in an unspecified order, probably not the order they were assigned in:
declare -A arr=([foo]=123 [bar]=456 [doo]=789)
for k in "${!arr[@]}"; do
echo "$k ${arr[$k]}"
done
Solution 3
You can simply loop over all values, no shifting needed:
folderArray=(sdb sdc sdd sde sdf sdg)
for folder in "${folderArray[@]}"; do
mkdir "/data/$folder"
done
Solution 4
You don't need any loop for that:
folderArray=(sdb sdc sdd sde sdf sdg)
IFS=,
eval mkdir /data/{"${folderArray[*]}"}
The trick is that if an array is double-quoted with subscript *
("${array[*]}"
) it expands to a single word with the value of each array element separated by the first character of the IFS
variable. After that we use brace expansion mechanism to attach /data/
to each array member and eval
uate the whole thing.
yael
Updated on September 18, 2022Comments
-
yael over 1 year
we want to build 6 mount point folders as example
/data/sdb /data/sdc /data/sdd /data/sde /data/sdf /data/sdg
so we wrote this simple bash script using array
folder_mount_point_list="sdb sdc sdd sde sdf sdg" folderArray=( $folder_mount_point_list ) counter=0 for i in disk1 disk2 disk3 disk4 disk4 disk5 disk6 do folder_name=${folderArray[counter]} mkdir /data/$folder_name let counter=$counter+1 done
now we want to change the code without counter and let=$counter=counter+1
is it possible to shift each loop the array in order to get the next array value?
as something like
${folderArray[++]}
-
RomanPerekhrest over 6 yearswhat's
for i in disk1 disk2 disk3 disk4 disk4 disk5 disk6
for as it's not being used within loop body?
-
-
yael over 6 yearscan we do the set for variable as set -- $list_of_folders ( while list_of_folders="sdb sdc sdd sde"
-
Hauke Laging over 6 years@yael Yes, you can use
set -- $list_of_folders
but again: String variables are not the way to go:set -- "${folders[@]}"
-
Hauke Laging over 6 yearsWhy so complicated?
cd /data ; mkdir "${folderArray[@]}"
I have done the same before but I would not in a case like this. But +1 for the advanced approach. -
jimmij over 6 years@HaukeLaging Yes, that would be simpler in case of
mkdir
command. And and even array is not needed, justcd /data; mkdir abc def
as normal person would do. But could not be as simple for other tasks, so it is good to know how to quickly attach a string to each array element without a loop. -
Alessio over 6 yearswhy are you even using
set -- ....
? that hack is only needed in shells that don't support arrays - there's no need for it in a shell that does supports arrays.for folder_name in "${folderArray[@]}"; do ... ; done
is all that's needed. -
yael over 6 yearsjust one question , is there any choice to do something like - ${folderArray[++]}
-
yael over 6 yearsjust one question , is there any choice to do something like - ${folderArray[++]}
-
jimmij over 6 years@yael You can do something like
echo "${folderArray[((counter++))]}"
if you really like this approach. Stuff inside(())
is evaluated as math (notice lack of$
in front ofcounter
). -
Alessio over 6 yearsand when you start writing shell code like that, you realise that putting a little time into learning perl or python would be a good idea. i.e. just because you can do something with bash, doesn't mean you should.
-
PesaThe over 6 years@cas that's what I have in my answer. I don't get this
set
approach either. -
Hauke Laging over 6 years@cas The OP explicitly asked for "shift". But
shift
works with positional parameters only. Of course, maybe I misunderstood the OP by taking that expression literally. -
PesaThe over 6 years@HaukeLaging I think OP just wanted to get rid of the counter while preserving the same functionality. That doesn't necessarily mean OP wants to shift the array. I think it's just an unlucky phrasing :)
-
U. Windl about 3 yearsStrictly speaking you are not shifting the array, but re-assigning it to a subset of itself. For large arrays that may be inefficient.
-
ilkkachu about 3 years@U.Windl, that's probably why the "shift" there is in quotes. And the answer does say that indexing as usual might be better... But since there's no operation to directly shift an array in the shell (it's not Perl), that's what you can do. If you have a large dataset and care about performance, you probably shouldn't use the shell to begin with.
-
U. Windl about 3 yearsWell there's "almost" a shift operator. See unix.stackexchange.com/a/648243/320598
-
ilkkachu about 3 years@U.Windl, as you noticed, it doesn't shift the indexes, just removes one of them. The only point for shifting I can see is to be able to point to the first element using an unchanging index. With the positional parameters, you can do that, since after a
shift
, the new first arg is$1
. Similarlyshift @a
in Perl makes$a[0]
the new first element of the array. But that doesn't work withunset "x[0]"
, you still need to keep track of the index of the element you want to access, and then you don't even need the "shifting". So, just indexing as usual might be better. -
Steffen Heil over 2 yearsInstead of
i=0; while [ "$i" -lt "${#a[@]}" ]; do
it would be simpler and probably faster to dofor i in {0..$((${#a[@]}-1))}; do
. -
ilkkachu over 2 years@SteffenHeil, maybe. The problem is that it pretty much only works in ksh as Bash does brace expansion before variable expansion (rather a useless order, IMO, but here we are).
{1..${#a[@]}}
works in zsh, though, and one could use"${!a[@]}"
also with a regular array (in Bash and ksh).