You can concatenate strings quoted differently (or not quoted at all). E.g. in "screen-${i}-"iter'-${j}'
there is double-quoted screen-${i}-
, unquoted iter
and single quoted -${j}
. This way you can tell the shell to expand ${i}
but not ${j}
. If the value of $i
is 1
then the resulting word will be screen-1-iter-${j}
.
In your particular case where $i
takes numerical values and $IFS
is the default one, just mixing the quotes will work.
However, with bash -c
(or sh -c
or ssh
or anything that takes shell code as an argument) embedding a variable expanded by the outer shell is as flawed as embedding {}
expanded by find
or xargs
, especially when the variable comes from an untrusted source.
For example if the value of your $i
was ; rm -f /important/file;
and you let the outer shell expand it and embed the result in the string that is about to be interpreted as shell code, then bash -c
spawned by screen
would interpret code containing echo screen-; rm -f /important/file;
and it would try to remove the /important/file
.
With bash -c
(or sh -c
) the right general way is like this:
i=whateverbash -c ' for j in 1 2; do echo "screen-${1}-iter-${j}" done' my-bash "$i"
(where my-bash
is an arbitrary string). Now the expanded value of $i
is passed to bash
as an extra argument. Inside the shell code you retrieve it with ${1}
(or $1
). The inner shell will not interpret the value as shell code.
Always prefer static shell code; pass variables as arguments or in the environment.
Also note I properly double-quoted$i
for the outer shell and screen-${1}-iter-${j}
for the inner shell. In your first snippet the outer shell sees unquoted screen-${i}
and the inner shell sees unquoted screen-${i}-iter-${j}
. You get away with this because 1
and 2
are safe values (at least with the default $IFS
). Still, IMO it's easier and less error-prone to always quote right by habit than to think each time if possible values are safe.