The following works in a Bash script:
PACKAGES=(
'curl'
'git'
'htop'
'mc'
'tree'
'vim'
)
apt --yes install ${PACKAGES[@]}
But how can I do the same in POSIX so that I can use it in a #!/bin/sh
script?
The following works in a Bash script:
PACKAGES=(
'curl'
'git'
'htop'
'mc'
'tree'
'vim'
)
apt --yes install ${PACKAGES[@]}
But how can I do the same in POSIX so that I can use it in a #!/bin/sh
script?
In a POSIX sh
shell, you have exactly one array: $@
(the array of positional parameters, i.e. $1
, $2
, ...). You set its values with set
:
set -- curl git htop mc tree vim
or from the command line:
./myscript.sh curl git htop mc tree vim
Then,
apt --yes install "$@"
Quoting the expansion of $@
makes the array expand to its quoted elements. That is, if the $@
array contains a word
and another word
, "$@"
will expand to those two strings. Not quoting $@
will make it expand to the four strings a
, word
, another
and word
. The unquoted behaviour depends on the contents of $IFS
.
Note that in bash
too, you'd like to double quote the ${PACKAGES[@]}
expansion:
apt --yes install "${PACKAGES[@]}"
Related: Arrays in Unix Bourne Shell
You don't really "join the elements with spaces" here though. Yes, if you echo
the values you'll get spaces between them, but it's the difference between
set -- a b c d
printf '>%s<\n' "$@"
which yields
>a<
>b<
>c<
>d<
(four separate arguments, which is what you want)
and
printf '>%s<' "$*"
which yields
>a b c d<
(one single argument with the elements joined by spaces (the first character of $IFS
), which is not what you actually want to use with your apt install
command)
dash
import it from the environment if there, most other shells ignore a IFS environment variable for security reasons.
– Stéphane Chazelas
Apr 19 '18 at 17:19
Since what you have is package names, you could just store them in a simple string variable, if you want to use a standard shell. At least according to Debian manuals, the package names can only contain the characters -+.:~a-z0-9A-Z
, none of which is a glob character or whitespace.
So, this should be ok:
#!/bin/sh
packages="curl git htop ..."
IFS=" " # make sure there's nothing weird here
apt --yes install $packages # no quotes!
If you're paranoid, add set -f
to the start to disable file name globbing. That would leave only spaces as an issue, but I don't think any sane system would allow whitespace in package names.
dash
inherit $IFS
from the environment, so here you'd want to also fix $IFS
to SPC (like every time you use that split+glob operator)
– Stéphane Chazelas
Apr 19 '18 at 17:16
/bin/sh
? i.e. is there some system withapt
that doesn't have Bash as a mandatory install anyway? (I know it'sEssential
in Debian and Ubuntu, but I don't know if there's some system that has gotten rid of it.) – ilkkachu Apr 19 '18 at 17:06apt
(not as one argument which contains the elements separated with space, whichapt
would consider as one package to install) – Stéphane Chazelas Apr 19 '18 at 17:14