2

The recommended pattern for bash as far as I know is to always quote the shell variables.
E.g. "$VAR" instead of $VAR.
But how can I achieve the same safety the quotes provide for variables meant to be interpreted remotely?
E.g in the following:

ssh server.com<<CODE
TARGET="target dir"
COUNT= \$( ls /foo/bar/\$TARGET | wc -l )  
echo \$COUNT > count.txt    

CODE   

For the code to work I need to escape $COUNT and $TARGET.
But how do I achieve the same safety that the "$COUNT" or "$TARGET" provides for this specific case?

Update
I have pasted only the part that is problematic.
I have also other lines that the variables are defined outside of the heredoc so if I use <<'CODE' then the snippet breaks.
For a more complete example:

SOME_STRING="SOME VALUE"  
ssh server.com<<CODE  
echo $SOME_VALUE > test_file.txt  # <--- does not work if I use <<'CODE'
TARGET="target dir"
COUNT= \$( ls /foo/bar/\$TARGET | wc -l )  
echo \$COUNT > count.txt    

CODE   
  • It looks like you might have mixed up SOME_STRING with SOME_VALUE. – igal Oct 20 '17 at 13:15
1

It looks like here-docs leave double quotes in place. For example, consider the following command:

cat <<HEREDOC
"this will still be quoted"
HEREDOC

This will produce the following quoted string as its output:

"this will still be quoted:

So you should just be able to quote your variables as usual:

SOME_STRING="SOME VALUE"
ssh server.com<<CODE
echo "$SOME_VALUE" > test_file.txt
TARGET="target dir"
COUNT="\$( ls /foo/bar/\$TARGET | wc -l )"
echo "\$COUNT" > count.txt
CODE

Regarding quoting variables inside command substitutions, refer to the following post: Quoting within $(command substitution) in Bash

Note that I've copied your snippet verbatim, but it looks like you probably meant to use the variable SOME_STRING instead of SOME_VALUE. Here is a slightly modified version of your snippet that I can run locally with the expected results:

mkdir -p "/foo/bar/target dir"

SOME_STRING="SOME VALUE"
ssh localhost<<CODE
echo "$SOME_STRING" > test_file.txt
TARGET="target dir"
COUNT="\$( ls "/foo/bar/\$TARGET" | wc -l )"
echo "\$COUNT" > count.txt
CODE
  • The command substitution should be quoted? And what about the $TARGET in the command substitution? That is unquoted in your code – Jim Oct 20 '17 at 13:11
  • Tried that. Breaks the script – Jim Oct 20 '17 at 13:13
  • Yes, you would probably want to quote $TARGET as well. But my point is that the quoting isn't being affected by the here-doc, so there isn't a difference between the local and remote quoting in this situation. Whether or not you want to quote the command substitution or anything else is up to you. A general rule of thumb is to quote everything that you want treated as a single token. – igal Oct 20 '17 at 13:18
  • The snippet does not compile – Jim Oct 20 '17 at 13:19
  • Compile? You mean doesn't run as expected? – igal Oct 20 '17 at 13:22
-1

We seem to get variations on this question a lot over the last few days.

In this case, quote the here-document:

ssh server.com <<'CODE'
TARGET="target dir"
COUNT= $( ls /foo/bar/$TARGET | wc -l )  
echo $COUNT > count.txt    
then  
fi  
CODE  

The 'CODE' (as opposed to just CODE) will ensure that the here-document is passed to the utility as it is written, without expanding variables or performing command substitutions.

Note that you also have syntax errors in the here-document (spaces around = and no if ta match the then). You should also double-quote the expansions both $COUNT and $TARGET as usual.

ssh server.com <<'CODE'
TARGET="target dir"
COUNT=$( ls "/foo/bar/$TARGET" | wc -l )  
printf '%s\n' "$COUNT" >count.txt    
CODE 

If you are trying to count the number of files in a remote directory, use this instead:

ssh server 'find "/path/to/dir" -maxdepth 1 -type f -exec echo . \;' | wc -l

This will correctly count the number of files in /path/to/dir.

To write the result to a remote file:

ssh server 'find "/path/to/dir" -maxdepth 1 -type f -exec echo . \; | wc -l >file'

If the code that you need to execute remotely is very complex, then it may be better to create a script on the server to run it. The variable that you need to pass to the script can be passed on its command line:

ssh server ./script.sh "$SOME_VARIABLE"

The script:

#!/bin/sh

VAR="$1"

# do stuff
  • The reason I am not using singe quotes around the CODE is because the rest of the commands are not interpreted correctly. Without quotes it works – Jim Oct 20 '17 at 12:44
  • E.g. if I have the line echo $SOME_VAR > ~/test_file.txt if I use <<'CODE then the file is empty i.e. nothing is printed. And SOME_VAR is defined outside of the here doc – Jim Oct 20 '17 at 12:57
  • I updated post too – Jim Oct 20 '17 at 12:59
  • I have more code and the logic is more complex than just counting files. I just pasted the part that shows the issue – Jim Oct 20 '17 at 13:00
  • @Jim See updated end of my answer. – Kusalananda Oct 20 '17 at 13:06

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.