2

I have some commands I want to execute in a bash script.
Now when we use single quotes the variable is taken literal and not the value.
So doing e.g. ls "$SOME_DIR" is not the same as ls '$SOME_DIR'
How can we handle a mix of quotes and double quotes in a way that not everything is cluttered by escaping double quotes?
E.g. how to valid something like:

ssh server.com "mkdir \"/foo/bar/"$final"\""  

How can I avoid all these escapes since the longer the commands it becomes unreadable and very easy to break?

  • Hi, I have updated my solution. It was much easier than I thought initially! :) – Rastapopoulos Oct 16 '17 at 13:25
  • If possible, use a script. Or make the directory tree locally and rsync or tar c . | ssh tar x x -C /. – muru Oct 16 '17 at 13:34
3

After much playing around with heredocs and herestrings, it seems that the simplest solution would be this:

ssh server.com "mkdir '/foo/bar/$final'"

Yes, bash will do variable substitution of $final despite the single quotes, just like it would in echo "test'$final'".

Note: as pointed out by @muru, this will not work if $final contains a single quote.

  • To be honest that does not seem much of an improvement to me. I was hoping there was a way to avoid all the clutter from quotes – Jim Oct 16 '17 at 10:54
  • Yeah that's true... You could do it using a heredoc, but I find it even less readable. I edited my answer to include that possibility. – Rastapopoulos Oct 16 '17 at 11:04
  • "...'$var'" working fine depends on the contents of "$final". What if contained single quotes? – muru Oct 16 '17 at 13:33
  • Yes, you're right. That's the only case where it wouldn't work... – Rastapopoulos Oct 16 '17 at 13:47
  • The statement "bash will do variable substitution of $final despite the single quotes" sounds confusing. In fact it won't. What happens in this case, single quoted part of the command is being send to remote shell where it has no single quotes when arrived, thus $final gets expanded by remote shell. Single quotes protects the variable from local shell expanding. – Tagwint Oct 16 '17 at 15:38
1

Opposite quoting, (i.e. single quotes in double quotes, and vice versa), can substitute for escapes. Without using escapes or here documents, consider how to use echo to display this text:

He didn't look back, and said "It can't be helped."

Surrounding it with either double or single quotes won't work. Alternating opposite quotes do work:

echo "He didn't look back, and said "'"It can'"'t be helped."'"'

Output:

He didn't look back, and said "It can't be helped."

Unfortunately, the above echo is just as difficult to read as escapes would be.

The happy medium is to use opposite quotes and escapes both as needed to reduce the visual monotony:

echo "He didn't look back, and said \"It can't be helped.\""

Applied to the code:

ssh server.com 'mkdir "'"/foo/bar/$final"\"

This should work even if $final contains a '.

Unrolling the quote, we have:

  1. 'mkdir "', which preserves a space and an opening double quote.
  2. "/foo/bar/$final" which returns contents of $final prior to launching ssh.
  3. \" closing double quote, preserving dirname for ssh
  • I don't understand how this works or how I would properly apply this – Jim Oct 19 '17 at 7:53
0

I think the real need pretty much depends on where/when you want the variables to be expanded - before sending the commands, locally or after sending, remotely. Both cases are valid. Either way, if your command fits in one line, I'd suggest you using <<< redirection surrounding command with either double or single quote respectively

1. Resolving variables before sending command

final="defined_locally"; ssh server.com <<<"final="defined_remotely" ;mkdir /foo/bar/"$final" "

2. Resolving variables after sending command

final="defined_locally"; ssh server.com <<<'final="defined_remotely"; mkdir /foo/bar/"$final" '

First one will create

/foo/bar/defined_locally

and the second one will create

/foo/bar/defined_remotely

directory on the remote server

As you see there's no need in escaping in both cases untill you want to mix local and remote defined vars in one command.

UPD:

<<<

strictly speaking is not a redirection, but HERE-STRING

UPD2:

Thanks to @Rastapopoulos for pointing to the

Pseudo-terminal will not be allocated because stdin is not a terminal messages that accompanies the command execution

Although it does no harm

  • What is the <<<? – Jim Oct 16 '17 at 11:38
  • I've extended my answer with the reference to the document page – Tagwint Oct 16 '17 at 11:47
  • You would still need to call sh though, no? – Rastapopoulos Oct 16 '17 at 11:49
  • No explicit calls for sh requried, the remote command is executed by the default user's shell there. – Tagwint Oct 16 '17 at 11:52
  • Hmm, but what's your command in this case: <<< ...? I tried in my terminal and it does produce different results. – Rastapopoulos Oct 16 '17 at 11:55
0

Is this a job for the printf builtin?

ldo@theon:~> f='He didn'\''t look back, and said "It can'\''t be helped."'
ldo@theon:~> echo "$f"
He didn't look back, and said "It can't be helped."
ldo@theon:~> ssh hypatia echo "$f"
bash: -c: line 0: unexpected EOF while looking for matching `"'
bash: -c: line 1: syntax error: unexpected end of file
ldo@theon:~> ssh hypatia echo $(printf %q "$f")
He didn't look back, and said "It can't be helped."

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.