Introduction
Consider a tool like watch
that can take another command (e.g. ls -l
) like this:
watch ls -l
# or equivalently
watch 'ls -l'
If the command is more complicated, it's all about quoting/escaping if things like $variable
, *
, |
, ;
or &&
are interpreted by the current shell or get to watch
. These two commands are different:
watch echo "$$"
watch 'echo "$$"'
So are these:
watch date ; echo done
watch date \; echo done
ssh
is similar, it can take one or more arguments and built a command to be run on the server. It depends on quoting/escaping if the local shell interprets $variable
, |
and such, or the remote shell.
And there are commands like sh -c
which require a single argument containing code, but the need for quoting/escaping is similar. In fact watch
(or ssh
) builds this single argument for sh -c
or similar command.
Problem
I already have the exact command I want to provide to watch
or ssh
, or similar tool. The command is obtained from history or pasted into the command line or typed as if it's going to be executed directly; it's in my command line verbatim. Nothing in the command should be expanded or interpreted before it gets to watch
or similar tool. In case of watch
it means all the expansion and interpretation should occur later, periodically. In case of ssh
it means it should occur on the server.
Now I need to do two things:
- Add
watch
(or whatever) at the beginning. This is not a problem. I can do this last. Quote and/or escape the original command. In general the command can include single-quotes, so just embracing with single-quotes blindly is not a solution. I think I know the right general solution:
- replace every
'
with'"'"'
or with'\''
, - embrace the whole resulting string with single-quotes;
but it's tedious and error-prone to do this by hand.
- replace every
Question
Can I make Bash properly add one level of single-quoting/escaping to the entire command line on demand?
(Note: the question arose when I was answering this one.)