I am trying to get a dollar sign variable to be expanded in single quotes. Not sure what I am doing wrong. I have tried every way I can think of.
for i in `cat file1`
do
for j in `cat file2`
do
ssh $i 'systemctl is-enabled "${j}" ';
done
done
for i in `cat file1`
do
for j in `cat file2`
do
ssh $i 'systemctl is-enabled "$j" ';
done
done
for i in `cat file1`
do
for j in `cat file2`
do
ssh $i 'systemctl is-enabled \$j ';
done
done
for i in `cat file1`
do
for j in `cat file2`
do
ssh $i 'systemctl is-enabled \\$j ';
done
done
for i in `cat file1`
do
for j in `cat file2`
do
ssh $i 'systemctl is-enabled \\\$j ';
done
done
for i in `cat file1`
do
for j in `cat file2`
do
ssh $i 'systemctl is-enabled \\\\$j ';
done
done
Why single quotes anyway? They stop interpretation of the string quoted. If you want to pass something that changes, how about one of these:-
ssh $i "systemctl is-enabled ${j}"; # No quotes around the variable being passed
ssh $i "systemctl is-enabled '${j}'"; # Wraps single quotes around variable being passed, interpreted locally but not interpreted again remotely
ssh $i "systemctl is-enabled \"${j}\""; # Wraps double quotes around variable being passed, interpreted locally but could then be interpreted again remotely if the variable is a remote variable name or subshell
The loops you have are rather badly structure too. I know that ssh will gobble up your input file with a normal while read line; do ssh user@server 'printf "Hello world\n"'; done < input_file but you can open multiple input files to help you. Would you consider:-
while read -u11 outer_variable # Read from file descriptor 11
do
while read -u12 inner_variable # Read from file descriptor 12
do
ssh "${user}@${outer_variable}" "foo bar \"${inner_variable}\""
done 12< "${inner_variable_input_file}" # Supply input file on file descriptor 12
done 11< "${outer_variable_input_file}" # Supply input file on file descriptor 11
Obviously it's better to use meaningful variable names. It avoids confusion from re-using them, breaking something that calls your code but has the same names and means you can understand your code when you come back to it many years later. Using i or j might save a few characters typing, but will be worse in the long run.
A few comments allowed?
The way you programmed your script might not be the best use of resources:
for i in `cat file1` # useless use of cat, may fail on spaces in file1's lines, `...` deprecated
do
for j in `cat file2` # same as above; plus: opens, reads, closes file2 linecount1 times
do
ssh $i 'systemctl is-enabled "${j}" '; # creates and deletes a remote network login (linecount1 * linecount2) times
done
done
How about trying, given your shell offers
the mapfile command
"here documents",
and using
a while instead of a for loop
the $(...) for "command substitution" in lieu of the `...`
reading each file only once, and connecting to each node in file1 just once (only lightly tested, YMMV):
mapfile -t j < file2
while read i
do ssh ${i} <<-EOFSSH
$(for X in ${!j[@]}
do echo "systemctl is-enabled ${j[X]}"
done)
EOFSSH
done < file1
Please report back...
EDIT: or, if your shell offers "here strings", using "parameter expansion / Pattern substitution", try
mapfile -t j < file2
IFS=$'\n'
while read i
do ssh -T ${i} <<< "${j
[*]/#/systemctl is-enabled }"
done < file1