Is there a way to pass in a variable as an argument to a bash script and have it evaluated scoped by the bash script?


# cat /path/to/file/of/host/names

# dofor

while read host; do
    # recursively turns "ssh \$host hostname" from $@ into:
    # ssh bob hostname
    # ssh tom hostname
    # ssh joe hostname
    # etc...
    eval $CMD
done < $FILE


# dofor /path/to/file/of/host/names "ssh \$host hostname"

Then: I'd receive the output from running ssh host hostname for each hostname listed in /path/to/file/of/host/names. e.g.:

  • I cannot figure out what's the question here: the dofor code as posted already does what you're asking... – Riccardo Murri Oct 7 '11 at 10:15
  • @RiccardoMurri It does as long as, in invocation, the second parameter passed to the script (one that contains $host is enclosed in single quotes and the command does not try to steal access to stdin. – rozcietrzewiacz Oct 7 '11 at 12:45
  • Indeed, I suppose I was just unnecessarily escaping the $host. Sorry for that, I was certain I'd checked that that wasn't the reason it wasn't working. – frogstarr78 Oct 7 '11 at 12:59

You can use

exec 3<$FILE
while read -u 3 host; do
    $2 $host $3

Then if you have:

$ cat /tmp/hostnames

and run dofor.sh /tmp/hostnames ssh ls, it will run in sequence:

ssh some.host1.com ls
ssh another.host2.net ls
ssh some.host3.org ls
ssh some.host4.com ls


If you'd like to change ssh or ls into some longer commands (or parts of such), just use quotes:

dofor.sh /tmp/hostnames "ssh -p 23" "ls -lh /"


With the following script, you'll be able to use $host variable in as many places in your command as you want:

exec 3<$1
while read -u 3 host; do
    eval $2

(I made this one shorter - no useless introducing of $FILE variable.) The important part here is that you need to use single quotes around the command containing the $host variable:

 dofor.sh /tmp/hostnames 'echo "trying $host :"; ssh -p 23 myuser@$host "ls -lh /"'

But beware that it is dangerous to use eval (see l0b0's wall) because if the file /tmp/hostnames contained a command on some line, it would be executed. Better not use this as root!

| improve this answer | |
  • that's an alternate method sure, granted there's no way to ensure that the $2'th and $3'th arguments will always surround the argument that I've passed in, and want replaced by values from the file – frogstarr78 Oct 6 '11 at 18:52
  • So you mean to say you want to be able to use $host in different places in the command? But just once in each? – rozcietrzewiacz Oct 6 '11 at 19:05
  • not necessarily. I want the $argument, possibly multiple times, that I pass to the script to be evaluated in the context of the for loop – frogstarr78 Oct 6 '11 at 20:02
  • I see. If that's te main point, I'll think about it... Meanwhile, I corrected the script to allow commands like ssh to steal your input without breaking the while-read-loop. (The previous version would exit after executing ssh if it prompted for password.) – rozcietrzewiacz Oct 6 '11 at 20:19
  • See the last edit - no need to use any substitution if you use single quotes. – rozcietrzewiacz Oct 6 '11 at 20:33

Try this:



source <({
cat $FILE | while read host; do
    echo $CMD | sed -e "s|\$host|$host|g"
| improve this answer | |
  • My initial description wasn't very clear – frogstarr78 Oct 6 '11 at 15:45
  • Does the this version get you what you are looking for? – k.parnell Oct 6 '11 at 16:25
  • yes, that would work, although it's pretty much what I came up w/ also. I was hoping to do something other than replacing values. I was hoping to take advantage of some sort of "scoping" feature of bash – frogstarr78 Oct 6 '11 at 18:56

This should work, but it's a really bad idea unless you completely control the contents of $FILE and it only contains very simple statements.

while read -r
done < $FILE

For the SSH example, you could do something much simpler by just looping over the host names:

while read -r host
    echo -n "${host}:"
    ssh "$host" hostname
done 9< "$FILE"

That should return a list of my hostname:remote hostname entries.

If you have a file with actual commands, you have to make sure whitespace is handled properly. The only way to be able to handle any parameters (including for example echo commands where the parameters are multiple lines) would be to use a custom format. One relatively simple way would be to separate each command and argument with a ␀ character, and put an extra ␀ character between the last argument of a command and the next command.

| improve this answer | |
  • I do control contents of that file and it is very basic – frogstarr78 Oct 6 '11 at 14:05
  • From the example given in the question, I understand that the commands would come from parameters passed to the script, not the file. But I admit the goal is quite unclear. – rozcietrzewiacz Oct 6 '11 at 14:58
  • @rozcietrzewiacz correct. I've updated the example in the description – frogstarr78 Oct 6 '11 at 15:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.