4

I am trying to write a bash script to print values of the 3 variables. Keep getting errors. What am I doing wrong?

INPUT1=/tmp/dir1
INPUT2=/tmp/dir2
INPUT3=/tmp/dir3

for i in 1 2 3
do 
echo $(INPUT$i)
done

When I run this script, tho output is:

syntax error: operand expected (error token is "/tmp/dir1

4 Answers 4

4

Bash doesn't support that kind of syntax directly. You could use 'eval', however with modern versions of 'bash' the cleanest way imho is via an array

input=( "/tmp/dir1" "/tmp/dir2" "/tmp/dir3" )

for i in {0,1,2}; do echo "${input[i]}"; done

(note that bash arrays are zero-indexed); or to list all elements you can use

for i in "${input[@]}"; do echo "$i"; done
3

I agree that using an array as shown in other answers is a better way to solve the real task of the OP.
But these answers do not directly answer the question as stated, so I'll do that:

Here is how you can do that with eval to access the variables, following closely the way the OP gave as his code:

INPUT1=/tmp/dir1
INPUT2=/tmp/dir2
INPUT3=/tmp/dir3

for i in {1..3} ; do
    eval "echo \$INPUT$i"
done

Output:

/tmp/dir1
/tmp/dir2
/tmp/dir3
0

The best way is to define INPUT variable as an array:

#!/bin/bash

INPUT[1]=/tmp/dir1
INPUT[2]=/tmp/dir2
INPUT[3]=/tmp/dir3

for i in "${INPUT[@]}"
do 
echo $i     #or $INPUT[$i]
done

See also: http://www.cyberciti.biz/faq/bash-for-loop-array/

6
  • This doesnt work. "for i in "${INPUT[@]}"". I get the following error : syntax error: operand expected (error token is ..
    – Ubuntuser
    Sep 4, 2013 at 7:02
  • @Ubuntuser Are you use bash shell as you tagged your question? Or how do you run the script? Sep 4, 2013 at 7:07
  • Yes its bash in Ubuntu 13.04. for i in {0,1,2}; works for me
    – Ubuntuser
    Sep 4, 2013 at 7:10
  • @Ubuntuser How do you run the script? Sep 4, 2013 at 7:12
  • ./script-name.sh is what I use.
    – Ubuntuser
    Sep 4, 2013 at 7:36
0

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/dir1INPUT1='/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).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .