0

I am trying to send a command to another terminal. I've learned that I need to use sh -c to send the entire command in once. The command itself is to compress a file using 7-Zip (command-line executable 7z). Here is an example:

7z a Aa.zip Bb.txt

So the entire command would be

sh -c '7z a Aa.zip Bb.txt'

This works without any issue. The problem is when there is a single quote (') in the filename to be compressed, e.g., B'b.txt. So, the command becomes

sh -c '7z a Aa.zip B'b.txt'

which does not run in the terminal.

These are the commands that I tried without any luck:

sh -c '7z a Aa.zip B'b.txt'
sh -c '7z a Aa.zip B\'b.txt'
sh -c '7z a Aa.zip B'"'"'b.txt'
sh -c '7z a Aa.zip "B'b.txt"'
sh -c '7z a Aa.zip \"B\'b.txt\"'
sh -c '7z a Aa.zip \"B'b.txt\"'
sh -c '7z a Aa.zip B'\''b.txt'

Running these commands result in either this error:

Syntax error: Unterminated quoted string

or waiting for input

>

which I then cancel using Ctrl + C.

I also tried using a variable and then pass it to sh -c. Again without any luck:

cmd="'7z a Aa.zip B'b.txt'"
echo $cmd
'7z a Aa.zip B'b.txt'
sh -c $cmd
a: 1: a: Syntax error: Unterminated quoted string

What am I doing wrong?

I know this question might sound familiar and may be similar to other questions like How can I escape quotes in command arguments to sh -c? and many others. But none of the methods marked as answer to these question work for me. So, please bear with me.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
NESHOM
  • 899
  • 16
  • 46

3 Answers3

3

You're looking for:

sh -c '7z a Aa.zip "B'\''b.txt"'

This: '\'' is an escaped ' as a part of the string. You need that for the sh command itself. Once you've started running the command, leaving the ' unmatched causes a problem, so you need to put it inside of a string.

cwallenpoole
  • 79,954
  • 26
  • 128
  • 166
0

I'll suggest reading through this shell command language article.

For your case, specifically

A backslash cannot be used to escape a single-quote in a single-quoted string. An embedded quote can be created by writing, for example: 'a'\''b', which yields a'b. A single token can be made up of concatenated partial strings containing all three kinds of quoting or escaping, thus permitting any combination of characters.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
cecky
  • 51
  • 4
  • Related: *[What do we do with answers that are entirely copied and improperly attributed (only a "reference" link or similar is included)?](https://meta.stackoverflow.com/questions/321299/)* – Peter Mortensen Aug 21 '23 at 17:43
0

Use

sh -c "7z a Aa.zip B'b.txt"

or

sh -c '7z a Aa.zip B'"'"'b.txt'

Or just, you know, use a (Bash or Z shell) array.

cmd=(7z a Aa.zip B'b.txt)
sh -c "${cmd[@]}"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • The array approach doesn't work here exactly as-given; it's equivalent to `sh -c '7z' 'a' 'Aa.zip' $'B\'b.txt'`, so all the words other than `7z` end up populating `$0`, `$1`, etc which are promptly ignored. – Charles Duffy Sep 14 '22 at 19:35
  • ...for bash 5.0+, maybe `sh -c "${cmd[*]@Q}"`? But you'd also need to fix the syntax used when _assigning_ the array, as the single quote isn't in double quotes or otherwise escaped, and is thus in a context where it's treated as syntactic rather than literal. – Charles Duffy Sep 14 '22 at 19:36
  • For your first solution, you have to escape the single quote so that `sh` should not interpret as a quote – wk_j Nov 23 '22 at 00:00