Variable issues in SSH - bash

Hey guys I'm trying to run this code:
#!/bin/bash
sudo /usr/local/bin/sshpass -p pwd ssh -o stricthostkeychecking=no -p 11022 admin#$1.test.com<<EOI
i=1
while read line
do
location="sudo sed -n ${i}p /Users/Shared/$1.txt"
number="sudo sed -n ${i}p /Users/Shared/$2n.txt"
my_array=("${my_array[i]}" $line)
sudo cp /Applications/TEPS\ OS\ X\ Share\ Folder/MAIN\ IMAGES\ FOLDER\ ƒ/${location}${number} /Users/Shared/FYP/$number
sudo sips -Z 256 /Users/Shared/FYP/$number /Users/Shared/FYP/$number
((i++))
done </Users/Shared/$2.txt
exit
EOI
basically it reads a text file which gives the location of certain images, and will create a thumbnail of those images, which can be downloaded later. The problem is that I need the value of $i to set the values of $location and $number, but when I set the variable within the while loop the variables are not set. I've tried setting it locally and globally with single quotes, double quotes, passing through with the sshpass, exporting it -This works as a test but $i is of course unknown- tried placing brackets, curly braces, parentheses, escaping $, at this point I have exhausted my ideas, it's probably something incredibly simple, but I could use a fresh pair of eyes, any help is greatly appreciated!
EDIT:
Thanks to Charles Duffy for helping me clean it up so this is what I have now:
#!/bin/bash
sudo /usr/local/bin/sshpass -p support ssh -o stricthostkeychecking=no -p 11022 admin#$1.noerrpole.com<<'EOI'
i=1
while read -r line
do
location=sudo sed -n ${i}p "/Users/Shared/$1.txt"
number=sudo sed -n ${i}p "/Users/Shared/$2n.txt"
my_array+=( "$line" )
sudo cp "/Applications/TEPS\ OS\ X\ Share\ Folder/MAIN\ IMAGES\ FOLDER\ ƒ/${location}${number}" "/Users/Shared/FYP/$number"
sudo sips -Z 256 "/Users/Shared/FYP/$number" "/Users/Shared/FYP/$number"
((i++))
exit
done <"/Users/Shared/$2.txt"
EOI
But now $2 isn't getting passed through to the loop here's what I get back
1:bin Photo$ bash -x thumb npco2 20131216154714
+ sudo /usr/local/bin/sshpass -p support ssh -o stricthostkeychecking=no -p 11022 admin#npco2.noerrpole.com
Pseudo-terminal will not be allocated because stdin is not a terminal.
SHPA_12-16-2013/
sed: /Users/Shared/n.txt: No such file or directory
cp: /Applications/TEPS OS X Share Folder/MAIN IMAGES FOLDER ƒ/ is a directory (not copied).
Warning: /Users/Shared/FYP/ not a valid file - skipping
Warning: /Users/Shared/FYP/ not a valid file - skipping
Error 4: no file was specified
Try 'sips --help' for help using this tool
So where $2 should equal 20131216154714 it's returning an empty string like this
sed: /Users/Shared/n.txt: No such file or directory
The correct command would be
sed: /Users/Shared/20131216154714n.txt
The rest is just failing because $2 isn't passed.
Again thanks for the help!

ssh ... <<EOI does expansion on the local end, before starting ssh. Use ssh ... <<'EOI' to do expansions on the remote end.
If you want to pass arguments, use printf '%q ' to quote them so they survive remote unescaping intact:
printf -v quoted_args '%q ' "$one" "$two"
ssh user#host "bash -s - ${quoted_args}" <<<'EOI'
...
EOI

Related

sed: There is no file or directory on ssh bash script

I want to save in a variable what the sed returns and then use it to compare with other text but when I try to save it I get this error.
This is the code:
#!/bin/bash
# Program name: test.sh
IPS=$HOME/Documentos/ips.txt
VERSION=/var/www/html/games/game/version
EOF=/dev/null
PASSWORD='password'
while read -r IP; do
echo $IP
sshpass -p $PASSWORD ssh -o 'StrictHostKeyChecking no' -o ConnectTimeout=1 -o ConnectionAttempts=1 $IP bash -c "'
REVISION=$(sed -n -e 6p $VERSION)
echo $REVISION
'" < $EOF
done < $IPS
This is the output:
192.168.232.69
sed: can't read /var/www/html/games/game/version: There is no file or directory
192.168.191.20
sed: can't read /var/www/html/games/game/version: There is no file or directory
192.168.191.19
sed: can't read /var/www/html/games/game/version: There is no file or directory
It should be clarified that when I use the command without saving it I do not have any errors and the file exist in every terminal.
Thanks to everyone for the help, I solved it using this link:
Assigning the value of a remote variable to a local variable in unix/linux

No such file or directory in Heredoc, Bash

I am deeply confused by Bash's Heredoc construct behaviour.
Here is what I am doing:
#!/bin/bash
user="some_user"
server="some_server"
address="$user"#"$server"
printf -v user_q '%q' "$user"
function run {
ssh "$address" /bin/bash "$#"
}
run << SSHCONNECTION1
sudo dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed" > /home/$user_q/check.txt
softwareInstalled=$(cat /home/$user_q/check.txt)
SSHCONNECTION1
What I get is
cat: /home/some_user/check.txt: No such file or directory
This is very bizarre, because the file exists if I was to connect using SSH and check the following path.
What am I doing wrong? File is not executable, just a text file.
Thank you.
If you want the cat to run remotely, rather than locally during the heredoc's evaluation, escape the $ in the $(...):
softwareInstalled=\$(cat /home/$user_q/check.txt)
Of course, this only has meaning if some other part of your remote script then refers to "$softwareInstalled" (or, since it's in an unquoted heredoc, "\$softwareInstalled").

running multiline bash command over ssh does not work

I need to run a multi-line bash command over ssh, all possible attempt exhausted but no luck --
echo "3. All files found, creating remote directory on the server."
ssh -t $id#$host bash -c "'
if [[ -d ~/_tmp ]]; then
rm -rf ~/_tmp/*
else
mkdir ~/_tmp
fi
'" ;
echo "4. Sending files ..."
scp ${files[#]} $id#$host:~/_tmp/ ;
Here is the output --
user#linux:/tmp$ ./remotecompile
1. Please enter your id:
user
2. Please enter the names of the files that you want to compile
(Filenames *must* be space separated):
test.txt
3. All files found, creating remote directory on the server.
Password:
Unmatched '.
Unmatched '.
Connection to host.domain.com closed.
Please note, I do not want to put every 2-3 lines of bash if-then-else-fi commands into separate files.
What is the right way to do it?
Use an escaped heredoc to have its literal contents passed through. (Without the escaping, ie. using just <<EOF, shell expansions would be processed locally -- making for more interesting corner cases if you used variables inside your remotely-run code).
ssh "$id#$host" bash <<'EOF'
if [[ -d ~/_tmp ]]; then
rm -rf ~/_tmp/*
else
mkdir ~/_tmp
fi
EOF
If you want to pass arguments, doing so in an unambiguously correct manner gets more interesting (since there are two separate layers of shell parsing involved), but the printf '%q' builtin saves the day:
args=( "this is" "an array" "of things to pass" \
"this next one is a literal asterisk" '*' )
printf -v args_str '%q ' "${args[#]}"
ssh "$id#$host" bash -s "$args_str" <<'EOF'
echo "Demonstrating local argument processing:"
printf '%q\n' "$#"
echo "The asterisk is $5"
EOF
This works for me:
ssh [hostname] '
if [[ -d ~/_tmp ]]; then
rm -rf ~/_tmp
else
mkdir ~/_tmp
fi
'

Bash - Escaping SSH commands

I have a set of scripts that I use to download files via FTP and then delete them from the server.
It works as follows:
for dir in `ls /volume1/auto_downloads/sync-complete`
do
if [ "x$dir" != *"x"* ]
then
echo "DIR: $dir"
echo "Moving out of complete"
# Soft delete from server so they don't get downloaded again
ssh dan#172.19.1.15 mv -v "'/home/dan/Downloads/complete/$dir'" /home/dan/Downloads/downloaded
Now $dir could be "This is a file" which works fine.
The problem I'm having is with special characters eg:
"This is (a) file"
This is a file & stuff"
tend to error:
bash: -c: line 0: syntax error near unexpected token `('
bash: -c: line 0: `mv -v '/home/dan/Downloads/complete/This is (a) file' /home/dan/Downloads/downloaded'
I can't work out how to escape it so both the variable gets evaluated and the command gets escaped properly. I've tried various combinations of escape characters, literal quotes, normal quotes, etc
If both sides are using bash, you can escape the arguments using printf '%q ', eg:
ssh dan#172.19.1.15 "$(printf '%q ' mv -v "/home/dan/Downloads/complete/$dir" /home/dan/Downloads/downloaded)"
You need to quote the whole expression ssh user#host "command":
ssh dan#172.19.1.15 "mv -v /home/dan/Downloads/complete/$dir /home/dan/Downloads/downloaded"
I'm confused, because your code as written works for me:
> dir='foo & bar (and) baz'
> ssh host mv -v "'/home/dan/Downloads/complete/$dir'" /home/dan/Downloads/downloaded
mv: cannot stat `/home/dan/Downloads/complete/foo & bar (and) baz': No such file or directory
For debugging, use set -vx at the top of the script to see what's going on.
Will Palmer's suggestion of using printf is great but I think it makes more sense to put the literal parts in printf's format.
That way, multi-command one-liners are more intuitive to write:
ssh user#host "$(printf 'mkdir -p -- %q && cd -- "$_" && tar -zx' "$DIR")"
One can use python shlex.quote(s) to
Return a shell-escaped version of the string s
docs

Bash: Insert escapes to variable containing spaces

Trying to copy a remote file to my local system using scp in bash
I've obtained the filename that i want and assigned to variable, $lastModifiedFile,
but the problem is it contains spaces in the filename.
To use this variable with scp the spaces need to be escaped with backslashes.
Is there an easy way to format this variable and insert the correct escape character where necessary i.e on spaces?
#!/bin/bash
lastModifiedFile=$(sshpass -p 'passw0rd' ssh user#server 'ls -tr /path/output*| tail -n 1')
echo "$lastModifiedFile"
sshpass -p 'passw0rd' scp user#server:"$lastModifiedFile" /root/
This is the script output ..
[user#host ~]# ./script.sh
/path/outputSat Mar 09 151905 GMT 2013.html
scp: /path/outputSat: No such file or directory
scp: Mar: No such file or directory
scp: 09: No such file or directory
scp: 151905: No such file or directory
scp: GMT: No such file or directory
scp: 2013.html: No such file or directory
I'm looking for something like below, or even a simpler solution? ..
escapedFilename=""
for letter in $lastModifiedFile
if $letter == " "
$escapedFilename += "\ "
else
$escapedFilename += $letter
With a bit of leaning toothpick syndrome:
param=user#server:${lastModifiedFile// /\\ /}
sshpass -p 'passw0rd' scp "$param" /root/
EDIT: It seems scp does not like me. I needed an additional level of variable in testing ... :)
EDIT 2: According to OP's feedback the exact solution appears to consist of using ${lastModifiedFile// /\\ \\}
I just hope there are no other characters than space that need escaping in some other filenames :)
Use single quotes around the filename passed to the remote system so that it is not subject to word splitting.
lastModifiedFile=$(sshpass -p 'passw0rd' ssh user#server 'ls -tr /path/output*| tail -n 1')
echo "$lastModifiedFile"
sshpass -p 'passw0rd' scp user#server:"'$lastModifiedFile'" /root/
or
sshpass -p 'passw0rd' scp "user#server:'$lastModifiedFile'" /root/
Just do it like this:
sshpass -p 'passw0rd' scp 'user#server:$lastModifiedFile' /root/
Here are a couple of methods that should handle almost anything (not just spaces) in the filename. First, bash's printf builtin has a %q format that adds quotes/escapes/whatever to the string:
sshpass -p 'passw0rd' scp user#server:"$(printf %q "$lastModifiedFile")" /root/
Note, however, that this quotes/escapes/etc it suitably for interpretation by bash. If the remote computer's default shell is something else, this may not work in all cases.
Option two is simpler in principle (but a bit messy in practice), and should be compatible with more remote shells. Here, I enclose the filename in single-quotes, which should work for anything other than single-quotes within the filename. For those, I substitute '\'' (which ends the single-quoted string, adds an escaped single-quote, then restarts the single-quoted string):
repl="'\''" # Have to store this in a variable to work around a bash parsing oddity
sshpass -p 'passw0rd' scp user#server:"'${lastModifiedFile//\'/$repl}'" /root/

Resources