3

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?

2
  • Hi, I have updated my solution. It was much easier than I thought initially! :) Oct 16, 2017 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, 2017 at 13:34

4 Answers 4

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.

6
  • 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, 2017 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. Oct 16, 2017 at 11:04
  • "...'$var'" working fine depends on the contents of "$final". What if contained single quotes?
    – muru
    Oct 16, 2017 at 13:33
  • Yes, you're right. That's the only case where it wouldn't work... Oct 16, 2017 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, 2017 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
1
  • I don't understand how this works or how I would properly apply this
    – Jim
    Oct 19, 2017 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

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .