I find that when writing text as input to another program, any command substitutions in double quotes within the intended text are interpreted and expanded by the shell
The links in the answer here states that single quotes can be used to prevent parameter expansion or command substitution. However I'm finding that enclosing a command substitution in single-quotes also fails to stop the shell from expanding the command substitution
How do you prevent the shell from interpreting command substitutions that are intended as text rather than a command to be executed?
A demonstration
$ echo "`wc -l *`"
attempts to count lines in all files in the current directory
$ echo "'`wc -l *`'"
Same result, i.e. counts lines in all files in the current directory
update From this demonstration I've spotted that the problem seems to be that I am quoting the single quotes. I think enclosing single quotes and `
(backtick) in double quotes preserves the literal meaning of (i.e. suppresses) the single quotes but does not preserve the literal meaning of the backquote (i.e. backtick) that introduces the command substitution.
In my use case the input for another command needs to be quoted. With this document saying that:
A single-quote cannot occur within single quotes
How do you prevent a single-quoted command substitution from being expanded when the single-quoted command substitution is within a (double) quoted string? There should be a way to do it other than using backslash escapes
Actual situation
In a program I'm using the only way to split a description of a task into separate lines is to enclose the description in double-quotes:
$ task add "first line doesn\'t say much
Second line says a lot but part of this line does not appear in the resulting description 'truncate -s0 !(temp_file | temp_dir)' truncates all files to 0 bytes as shown by: '`wc -l *`'"
The resulting description:
first line doesn\ -s0 !(temp_file | temp_dir)' truncates all files to 0 bytes as shown by: 0 file1 10 file2 0 directory1 0 directory2 502 file3 123 file4 162 file5 0 directory3
As you can see
't say much
Second line says a lot but part of this line does not appear in the resulting description 'truncate
is missing from the description and the shell has interpreted 'wc -l *'
as a command substitution, thereby including the line counts of all files in the current directory as part of the description
What's causing the shell to remove the part of the argument to task
between \
(backslash) and -s
, and how do you prevent the shell from interpreting the above single-quoted command substitution (i.e. '`wc -l *`'
)?
Best Answer
Here (linebreaks added),
the whole string is double-quoted, so command substitutions and other expansions will run there. That happens in the shell, before
task
sees that string, and you'll need to prevent it with backslashes or putting that part in single quotes.E.g.
So,
would pass the string
...shown by: '`wc -l *`'
totask
. It's up to it what does with that.If you don't want to use backslashes, here's the way to put it in single quotes:
(The
a
's mark the double-quoted parts, theB
's the single-quoted parts. They are just concatenated on the shell command line. The literal single quotes are within the double-quoted strings.)As for the single quote and the backslash, you don't need to escape a single quote within double quotes, and in fact the backslash will remain there:
From what you show, it seems like
task
removes at least the first single-quoted string from the argument (plus a word after that, since the removed part was't say much ... 'truncate
)The shell will not do that, this works fine:
It's highly likely it's not the shell doing that.
It's not single-quoted, it's double-quoted with quoted single quotes next to it.