File1.txt contains a list of hosts:


File2.txt contains a list of commands...

ping host_name -c 10
dig host_name A host_name AAAA +short 

...to be executed on hosts. Before the commands can be executed, sed -e "s/host_name/$line/" $2 substitutes the host_name string in file2 w/ the actual hostnames from File1. I end up w:

ping computer_1 -c 10
dig computer_1 A computer_1 AAAA +short 
ping computer_2 -c 10
dig computer_2 A computer_2 AAAA +short 

How do I execute this output as a command?

I've tried a couple of different noob ways unsuccessfully including:

while read line; do
    commands="$(sed -e "s/host_name/$line/" $2)"
    eval $commands
done < $1
  • 1
    Hi hernoid, this really looks like a XY problem. Could you describe what you are trying to achieve with that?
    – Quasímodo
    May 1 '20 at 20:14
  • I've edited my question clarifying this further.
    – hernoid
    May 1 '20 at 20:57
  • But sed will replace all instances of host_name the first time is run... making the loop unuseful... could you post the expected result, as well as a real sample of the files you are working with? May 1 '20 at 21:05

I suspect this is simply an issue of quotes in quotes not working as expected.

The syntax highlighting for your code gives a hint to what's going on. Notice how s/host_name/$line/ is black instead of being red like the rest of the string? This is because you've actually created two strings with some garbage that bash doesn't understand in the middle.

Something like this should work

while read line; do
    commands=$(sed -e "s/host_name/$line/" $2)
    eval "$commands"
done < $1

You can have both a while read loop and a for loop

A foor loop but it requires mapfile aka readarray

mapfile -t commands < file2.txt
mapfile -t hosts < file1.txt

for h in "${hosts[@]}"; do
  for c in "${commands[@]}"; do
    echo  command "$c" "$h"

A common work around if your bash is not recent enough to have mapfile

IFS=$'\n' read -rd '' -a array < file.txt


while IFS=  read -r file; do
done < file.txt

Now "${array[@]}" holds all the values/elements.

A while read loop.

while IFS= read -ru "$fd" h; do
  while IFS= read -r c; do
    echo command "$c" "$h"
  done < file2.txt
done {fd}< file1.txt
  • Remove the echo if you think the output is correct, so It can actually execute the commands.
  • Assuming both file has one entry per line the builtin command should suffice, meaning there are no ; to separate multiple commands per line. Other wise yeah... eval

  • The $fd and {fd} is an arbitrary name and uses an arbitrary file descriptor, if your bash is not recent enough to have it change both to 3 or higher, since 0 to 2 is already taken. It is needed to use a different fd just in case something in the while read loop is eating/slurping stdin like for example ssh.

  • I should have mentioned, the commands file (File2) will have a command more like: ping host_name -c 10 which is why I was using sed to substitute host_name within each command w/ actual hostname found in hosts file (File1). I mention this b/c in your for loop within a for loop, echo command "$c" "$h" won't work my case without the substitution
    – hernoid
    May 3 '20 at 20:20
  • As suggested on your post by someone, add the input and expected output just to be clear.
    – Jetchisel
    May 3 '20 at 20:31
  • how do I make this work when my command is more like ping host_name -c 10 and expected result ping computer_1 -c 10
    – hernoid
    May 7 '20 at 3:18

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.