dqbydt
dqbydt

Reputation: 73

How can I escape double quotes in the system command in this perl oneliner?

For my edification, I'd like to find out how to escape quotes within the system command in the following perl oneliner:

$ ls -p | grep -v / | perl -lane 'system("echo \"$_\"");'

without using the \" escape sequence, in the manner shown here: https://stackoverflow.com/a/1250279/3367247

(Obviously this command does nothing useful; it just prints filenames that have spaces in them. But it serves to explain my problem succintly.)

I tried the following (pardon the images, but I have found highlighting easiest to decipher the quoting):

  1. system command with double quotes: system("echo "$_"") enter image description here

Code: $ ls -p | grep -v / | perl -lane 'system("echo ''"''$_''"''");'

  1. system command with single quotes: system('echo "$_"') enter image description here

Code: $ ls -p | grep -v / | perl -lane 'system('"'"'echo "$_"'"'"');'

Neither of these work. Is there a way to accomplish this in the manner of the linked answer?

EDIT: I need double-quotes around the $_ in the echo command because the string in $_ contains parentheses, not because it contains spaces, as I originally thought.

Upvotes: 2

Views: 2523

Answers (2)

dqbydt
dqbydt

Reputation: 73

I'd like to post some insights I have gleaned from the helpful comments and the accepted answer (thank you all!). In the original command I posed: $ ls -p | grep -v / | perl -lane 'system("echo \"$_\"");' there are two interpreters: bash, and perl, coming into play:

  1. The original command, interpreted by bash, doesn't touch anything within the (sole pair of) single quotes (i.e. everything after the -lane) and passes it intact as a script to perl.

  2. The perl interpreter then processes this script, which consists of the single system command. It is the perl interpreter that is responsible for generating the final echo command line which is tossed back to bash for interpretation. This final bash command needs to be dynamically generated from $_. There are two ways to do this:

    (a) Use the system command with double quotes so that the $_ gets interpolated [i.e. system("echo $_")] But note, the string echoed needs to have "s around it because it could contain parentheses. So we need perl to emit, in this final bash command string, literal "s, around the $_ (which will get expanded by virtue of the outer "s, prior to being sent to bash). There are two ways now to accomplish this:

    (a-i) Escape the " using a \ to emit literal quotes: "echo \"$_\""

    (a-ii) Use the qq operator, which demotes the special status accorded to "s within a perl string, and which then can be used as normal characters: qq(echo "$_").

    (b) - OR - the other way of generating the final bash command is to use single quotes in the system command as suggested by @Håkon in his answer: system('echo ' . $_). But we still do need the quotes around $_ as discussed earlier, so we need to change this to: system('echo "' . $_ . '"'). Now the task reduces to figuring out bash's single-quote escapes to make sure perl sees this string.

The final forms of the perl command are:

2-a-i: perl -lane 'system("echo \"$_\"");'

2-a-ii: perl -lane 'system(qq/echo "$_"/);'

And finally:

2-b: perl -lane 'system('"'"'echo "'"'"'.$_.'"'"'"'"'"');'

This perversion of 's and "s is what I set out to attain, so MISSION ACCOMPLISHED! :D

Editorial note: From a practical, usability pov, the qq form seems most amenable to just pasting the final, desired bash command into the perl system call (and you can then just replace filenames with $_).

Upvotes: 1

Håkon Hægland
Håkon Hægland

Reputation: 40718

I'd like to find out how to escape quotes within the system command in the following perl oneliner:

$ ls -p | grep -v / | perl -lane 'system("echo \"$_\"");'

without using the \" escape sequence, in the manner shown here: [...]

Those examples are for escaping single quotes, your example uses double quotes \"$_\". In Bash everything within single quote are taken literally (except the single quote itself), the same is not true for that within double quotes. For example a $ within double quotes are not a literal $ but indicates that a variable expansion is to follow.

Now, in your command there are three levels of quote interpretation.

  • First the outer shell (the command line):

    perl -lane 'system("echo \"$_\"");'
    
  • Then the perl interpreter:

    system("echo \"$_\");
    
  • Then a second shell (here, Bash is used):

    echo "xxx"
    

where xxx is the expansion of $_ in the perl script

Your command does not contain any internal single quotes, so there is no need to use the Bash quoting constructs for escaping single quotes, like

It's fine  -> 'It'"'"'s fine'

or

It's fine  -> 'It'\''s fine'

Further, you need to escape the double quote in the perl script

system("echo \"$_\"")

since they are inside double quotes, alternatively you could remove them since echo does not need double quotes here:

system("echo $_")

would work fine, or you could do as proposed in the comments:

system(qq/echo "$_"/)

or use the LIST form of system with more than one argument

system("/bin/echo", $_)

However, if you wanted to use single quotes, for example:

system('echo ' . $_)

you would need to invoke the shell's single quote escapes:

'system('"'"'echo ''"' . $_)'

or (using double quotes):

"system('echo ' . "'$'"_)"

Upvotes: 0

Related Questions