This question already has answers here:
Passing external shell script variable via ssh
(2 answers)
Closed 6 years ago.
I have a script parsing a list of servers, looking for some stuff and executing commands if the circumstances are correct.
The main server is connecting to them via ssh, executing all commands that are in the EOF statement:
#!/bin/bash
# parsing servers
# defining one local variable $VAR
ssh -T -p 1234 root#"server-ip" "$variable" << 'EOF'
# doing some stuff...
var_result=$(mysql -hhost -uuser '-ppasswort' -Ddatabase -N -e "SELECT something FROM somewhere WHERE value=$VAR;")
EOF
I know the variable can pass through if I remove the single quotes from the EOF, but if i do so the mysql statements wont work and everything breaks.
I know there are ways to transmit a variable, but things with ";" between options wont work for me ( the script tries to execute it as a command )
Any ideas?
Use printf %q to escape content in an eval-safe form; after doing so, you can pass them on the command line of the remote shell, and retrieve them via $1, $2, etc. within the remote script:
# put contents of $VAR into $var_str in a format that a shell can interpret
printf -v var_str %q "$VAR"
# v- pass the value on the shell command line
# | v- keep escaping the heredoc securely
# | |
ssh -T -p 1234 root#"$host" "bash -s $var_str" <<'EOF'
# retrieve it off the shell command line
var=$1
# ...and use it as you like thereafter.
echo "Remotely using $var"
EOF
How about using EOF without the quote and making the mysql command work:
#!/bin/bash
# parsing servers
# defining one local variable $VAR
VAR=something
ssh -T -p 1234 root#"server-ip" <<EOF
# doing some stuff...
var_result=\$(mysql -hhost -uuser '-ppasswort' -Ddatabase -N -e "SELECT something FROM somewhere WHERE value=$VAR;")
EOF
As Charles Duffy stated, this may produce some security risk.
Another way is to wrap all your codes around single quote:
#!/bin/bash
# parsing servers
# defining one local variable $VAR
ssh -T -p 1234 root#"server-ip" '
# doing some stuff...
var_result=$(mysql -hhost -uuser "-ppasswort" -Ddatabase -N -e "SELECT something FROM somewhere WHERE value='"$VAR"';")
'
In this case you will have to be careful what you substitute your variables for. Better use Charles Duffys' method if you should be concerned about it.
Related
This question already has answers here:
Passing external shell script variable via ssh
(2 answers)
Closed 6 years ago.
I have a script parsing a list of servers, looking for some stuff and executing commands if the circumstances are correct.
The main server is connecting to them via ssh, executing all commands that are in the EOF statement:
#!/bin/bash
# parsing servers
# defining one local variable $VAR
ssh -T -p 1234 root#"server-ip" "$variable" << 'EOF'
# doing some stuff...
var_result=$(mysql -hhost -uuser '-ppasswort' -Ddatabase -N -e "SELECT something FROM somewhere WHERE value=$VAR;")
EOF
I know the variable can pass through if I remove the single quotes from the EOF, but if i do so the mysql statements wont work and everything breaks.
I know there are ways to transmit a variable, but things with ";" between options wont work for me ( the script tries to execute it as a command )
Any ideas?
Use printf %q to escape content in an eval-safe form; after doing so, you can pass them on the command line of the remote shell, and retrieve them via $1, $2, etc. within the remote script:
# put contents of $VAR into $var_str in a format that a shell can interpret
printf -v var_str %q "$VAR"
# v- pass the value on the shell command line
# | v- keep escaping the heredoc securely
# | |
ssh -T -p 1234 root#"$host" "bash -s $var_str" <<'EOF'
# retrieve it off the shell command line
var=$1
# ...and use it as you like thereafter.
echo "Remotely using $var"
EOF
How about using EOF without the quote and making the mysql command work:
#!/bin/bash
# parsing servers
# defining one local variable $VAR
VAR=something
ssh -T -p 1234 root#"server-ip" <<EOF
# doing some stuff...
var_result=\$(mysql -hhost -uuser '-ppasswort' -Ddatabase -N -e "SELECT something FROM somewhere WHERE value=$VAR;")
EOF
As Charles Duffy stated, this may produce some security risk.
Another way is to wrap all your codes around single quote:
#!/bin/bash
# parsing servers
# defining one local variable $VAR
ssh -T -p 1234 root#"server-ip" '
# doing some stuff...
var_result=$(mysql -hhost -uuser "-ppasswort" -Ddatabase -N -e "SELECT something FROM somewhere WHERE value='"$VAR"';")
'
In this case you will have to be careful what you substitute your variables for. Better use Charles Duffys' method if you should be concerned about it.
When I stumble across an evil web site that I want blocked from corporate access, I edit my named.conf file on my bind server and then update my proxy server blacklist file. I'd like to automate this somewhat with a bash script. Say my script is called "evil-site-block.sh" and contains the following:
ssh root#192.168.0.1 'echo "#date added $(date +%m/%d/%Y)" >> /var/named/chroot/etc/named.conf; echo "zone \"$1\" { type master; file \"/etc/zone/dummy-block\"; };" >> /var/named/chroot/etc/named.conf'
It is then run as
$ evil-site-block.sh google.com
When I look at the contents of named.conf on the remote machine I see:
#date added 09/16/2014
zone "" { type master; file "/etc/zone/dummy-block"; };
What I can't figure out is how to pass "google.com" as $1.
First off, you don't want this to be two separately redirected echo statements -- doing that is both inefficient and means that the lines could end up not next to each other if something else is appending at the same time.
Second, and much more importantly, you don't want the remote command that's run to be something that could escape its quotes and run arbitrary commands on your server (think of if $1 is '$(rm -rf /)'.spammer.com).
Instead, consider:
#!/bin/bash
# ^ above is mandatory, since we use features not found in #!/bin/sh
printf -v new_contents \
'# date added %s\nzone "%s" { type master; file "/etc/zone/dummy-block"; };\n' \
"$(date +%m/%d/%Y)" \
"$1"
printf -v remote_command \
'echo %q >>/var/named/chroot/etc/named.conf' \
"$new_contents"
ssh root#192.168.0.1 bash <<<"$remote_command"
printf %q escapes data such that an evaluation pass in another bash shell will evaluate that content back to itself. Thus, the remote shell will be guaranteed (so long as it's bash) to interpret the content correctly, even if the content attempts to escape its surrounding quotes.
Your problem: Your entire command is put into single quotes – obviously so that bash expressions are expanded on the server and not locally.
But this also applies to your $1.
Simple solution: “Interupt” the quotation by wrapping your local variable into single quotes.
ssh root#192.168.0.1 'echo "#date added $(date +%m/%d/%Y)" >> /var/named/chroot/etc/named.conf; echo "zone \"'$1'\" { type master; file \"/etc/zone/dummy-block\"; };" >> /var/named/chroot/etc/named.conf'
NB: \"$1\" → \"'$1'\".
NOTE: This solution is a simple fix for the one-liner as posted in the question above. If there's the slightest chance that this script is executed by other people, or it could process external output of any kind, please have a look at Charles Duffy's solution.
I am using the below command on the local machine and it gives me the expected result:
sed -n 's/^fname\(.*\)".*/\1/p' file.txt
When I use the same command(only changed ' to ") to a same file present in the remote system, I do not get any output.
ssh remote-system "sed -n "s/^fname\(.*\)".*/\1/p" file.txt"
Please help me to get this corrected. Thanks for your help.
" and ' are different things in bash, and they are not interchangeable (they're not interchangeable in many languages, however the differences are more subtle) The single quote means 'pretend everything inside here is a string'. The only thing that will be interpreted is the next single quote.
The double quote allows bash to interpret stuff inside
For example,
echo "$TERM"
and
echo '$TERM'
return different things.
(Untested) you should be able to use single quotes and escape the internal single quotes :
ssh remote-system 'sed -n \'s/^fname(.)"./\1/p\' file.txt'
Looks like you can send a single quote with the sequence '"'"' (from this question)
so :
ssh remote-machine 'sed -n '"'"'s/^fname\(.*\)".*/\1/p'"'"' file.txt'
This runs on my machine if I ssh into localhost, there's no output because file.txt is empty, but it's a proof-of-concept.
Or - can you do the ssh session interactively/with a heredoc?
ssh remote-system
[sed command]
exit
or (again untested, look up heredocs for more info)
ssh remote-system <<-EOF
[sed command]
EOF
there is a problem with the invoked via ssh bash, although i have read mans about it i still can't explain the following:
Here is a script, very simple
#!/bin/bash
theUser=$1
theHost=$2
ssh -tt $theUser#$theHost 'bash' << EOF
a=1
echo 'dat '$a
exit
EOF
and here is the result:
victor#moria:~$ bash thelast.sh victor 10.0.0.8
victor#10.0.0.8's password:
a=1
echo 'dat '
exit
victor#mordor:~$ a=1
victor#mordor:~$ echo 'dat '
dat
victor#mordor:~$ exit
exit
Connection to 10.0.0.8 closed.
As you may see, the environment doesn't store the value of the variable "a" so it can't echo it, but any other commands like ls or date return the result.
So the question is what i am doing wrong and how to avoid such behavior?
p.s. i can't replace ssh -tt, but any other command may be freely replaced.
Thanks in advance
Inside the here document, the $a is expanded locally before feeding the input to the ssh command. You can prevent that by quoting the terminator after the << operator as in
ssh -tt $theUser#$theHost 'bash' << 'EOF'
$a is being expanded in the local shell, where it is undefined. In order to prevent this from happening, you should escape it:
echo "dat \$a"
Escaping the $ causes it to be passed literally to the remote shell, rather than being interpreted as an expansion locally. I have also added some double quotes, as it is good practice to enclose parameter expansions inside them.
This is what I am trying to do...
#!/bin/bash
array_local=(1 2 3 4 5)
ssh user#server << EOF
index_remote=1
echo \$index_remote
echo \${array_local[\$index_remote]}
EOF
When I try to run the above script I get the O/P as 1 and a null value (blank space). I wanted ${array_local[$index_remote} value to be 2 instead of null, I need to access this local array using remote variable for my further work in the script..
<<EOF results variable expansion happening on the local machine, but you only defined the variable i on the remote machine. You need to think carefully about where you want to do the expansion. You haven't explained in your question whether the value of i is defined client-side or server-side, but I'm guessing from your subsequent comments that you want it done server-side. In that case you'll need to pass the array contents over ssh, which requires careful quoting:
ssh hostname#server <<EOF
i=1
eval `typeset -p array_local`
echo \${array_local[\$i]}
EOF
typeset -p array_local will output the string
declare -a array_local='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5")'
Since this is inside backticks, it will get expanded client-side within the EOF-delimited heredoc, and then evaluated server-side by the eval. In other words it's equivalent to:
ssh hostname#server <<'EOF'
i=1
declare -a array_local='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5")'
echo ${array_local[$i]}
EOF
Notice the difference in EOF quoting between the two examples. The first one allows parameter and shell expansion, and the second doesn't. That's why the echo line in the first one needs quoting, to ensure that parameter expansion happens server-side not client-side.