bash
actually supports something pretty close to what you first tried.
INPUT1=/tmp/dir1
INPUT2=/tmp/dir2
INPUT3=/tmp/dir3
for s in INPUT{1..3}; do
echo ${!s}
done
This works because:
INPUT{1..3}
expands to INPUT1 INPUT2 INPUT3
. (It's equivalent to INPUT{1,2,3}
.)
Where $s
is the expansion of s
, ${!s}
is the expansion of the expansion of s
.
For example, in the first iteration of the loop, if $s
appeared it would be expanded to INPUT1
. If $INPUT1
appeared it would be expanded to /tmp/dir1
. Thus ${!s}
is expanded to /tmp/dir1
.
This kind of parameter expansion is called indirect expansion. In this situation and many like it, the built-in !
syntax achieves this more compactly and elegantly than eval
.
For more information, see Is it possible to build variable names from other variables in bash? on Stack Overflow.
Some caveats (which also apply to some of the other answers that have been posted):
If any values of INPUTn
may contain whitespace1 (like spaces2, tabs, or newlines) or constructions with special characters that would be expanded (such as $varname
), quoting should be used.
Single quotes for the assignment are best if you want to prevent all expansion there:
INPUT1=/tmp/dir1
→ INPUT1='/tmp/dir with spaces'
(Only a '
quote character is treated specially, as it will act as the closing quote mark.)
But use double quotes to still allow the variable to be expanded:
echo ${!s}
→ echo "${!s}"
(This works even if the value after expansion contains a $
. The expanded contents are not expanded again.
For example, BAZ=QUUX FOO='BAR $BAZ'; echo "$FOO"
prints BAR $BAZ
, not BAR QUUX
.)
If any of INPUTn
might take on a value that echo
would interpret as an option instead of text to print, replace echo
with printf '%s\n'
.3
Currently this is -
followed by one more e
, E
, or n
. But you should probably consider anything starting with -
as dangerous in case future bash versions add new options.
Although, in terms of syntax, indirect expansion as presented above is nearly as simple as doing it with an array, you still may wish to use an array because:
- It may better reflect the underlying meaning of the problem you're trying to solve.
- You might have use for other features of arrays in solving this problem.
Perhaps you intended /tmp/dir1
, /tmp/dir2
, and /tmp/dir3
as opaque examples that may be replaced by anything.
But if you really want to make an array of those specific values, I recommend:
input=(/tmp/dir{1..3})
Similarly, if your goal is just to loop through /tmp/dir1
, /tmp/dir2
, and /tmp/dir3
and perform some action with each, then you don't need to store them in any kind of variable:
for s in /tmp/dir{1..3}; do
echo $s
done
If the values of s
will contain whitespace or special characters (see above), quote them, but leave the {...}
range unquoted by separately quoting the text to the left and right of it--it won't be expanded if quoted, even in double quotes.
for s in '/tmp/dir '{1..3}' with spaces'; do
echo "$s"
done
That prints:
/tmp/dir 1 with spaces
/tmp/dir 2 with spaces
/tmp/dir 3 with spaces
And if your goal is really just to print /tmp/dir1
, /tmp/dir2
, and /tmp/dir3
, each on its own line, then this single command3 is sufficient:
printf '%s\n' /tmp/dir{1..3}
1: The shell splits text into words on whitespace or, if the IFS
variable is defined, on the field separators it specifies. But you usually don't have to worry about IFS
unless you've set it yourself.
2: echo hello world
actually does work: it prints hello world
, just like echo 'hello world'
. This is because echo
prints a single space between each echoed argument. However, echo hello world
is not equivalent to echo 'hello world'
: the former still prints hello world
while latter prints hello world
.
3: For more information on bash
's printf
builtin, see the output of help printf
or this section of the Bash manual. printf
is also an executable that can be called from other shells (and in that capacity is standardized).