I ran into this problem while trying to print single quotes in a Perl one-liner. I eventually figured out you have to escape them with '\''
. Here's some code to illustrate my question.
Let's start with printing a text file.
perl -ne 'chomp; print "$_\n"' shortlist.txt
red
orange
yellow
green
blue
Now let's print the name of the file instead for each line.
perl -ne 'chomp; print "$ARGV\n"' shortlist.txt
shortlist.txt
shortlist.txt
shortlist.txt
shortlist.txt
shortlist.txt
Then we can add single quotes around each line.
perl -ne 'chomp; print "'$_'\n"' shortlist.txt
shortlist.txt
shortlist.txt
shortlist.txt
shortlist.txt
shortlist.txt
Wait that didn't work. Let's try again.
perl -ne 'chomp; print "'\''$_'\''\n"' shortlist.txt
'red'
'orange'
'yellow'
'green'
'blue'
So I got it working now. But I'm still confused on why '$_' evaluates to the program name. Maybe this is something easy but can someone explain or link to some documentation?
edit: I'm running Perl 5.8.8 on Red Hat 5
To your shell, 'chomp; print "'$_'\n"'
results in a string that's the concatenation of
chomp; print "
(the first sequence inside single quotes),
- the value of its variable
$_
, and
\n"
(the second sequence inside single quotes).
In bash
, $_
"... expands to the last argument to the previous command, after expansion. ...". Since this happens to be shortlist.txt
, the following is passed to perl
:
chomp; print "shortlist.txt\n"
For example,
$ echo foo
foo
$ echo 'chomp; print "'$_'\n"'
chomp; print "foo\n"
You use single quotes in one-liners to protect your Perl code from being evaluated by the shell. In this command:
perl -ne 'chomp; print "'$_'\n"' shortlist.txt
you close the single quotes before $_
, so the shell expands $_
to the last argument to the previous command. In your case, this happened to be the name of your input file, but the output would be different if you ran a different command first:
$ echo foo
$ perl -ne 'chomp; print "'$_'\n"' shortlist.txt
foo
foo
foo
foo
foo
I try to avoid quotes in one liners for just this reason. I use generalized quoting when I can:
% perl -ne 'chomp; print qq($_\n)'
Although I can avoid even that with the -l
switch to get the newline for free:
% perl -nle 'chomp; print $_'
If I don't understand a one-liner, I use -MO=Deparse
to see what Perl thinks it is. The first two are what you expect:
% perl -MO=Deparse -ne 'chomp; print "$_\n"' shortlist.txt
LINE: while (defined($_ = <ARGV>)) {
chomp $_;
print "$_\n";
}
-e syntax OK
% perl -MO=Deparse -ne 'chomp; print "$ARGV\n"' shortlist.txt
LINE: while (defined($_ = <ARGV>)) {
chomp $_;
print "$ARGV\n";
}
-e syntax OK
You see something funny in the one where you saw the problem. The variable has disappeared before perl
ever saw it and there's a constant string in its place:
% perl -MO=Deparse -ne 'chomp; print "'$_'\n"' shortlist.txt
LINE: while (defined($_ = <ARGV>)) {
chomp $_;
print "shortlist.txt\n";
}
-e syntax OK
Your fix is curious too because Deparse puts the variable name in braces to separate it from the old package specifier '
:
% perl -MO=Deparse -ne 'chomp; print "'\''$_'\''\n"' shortlist.txt
LINE: while (defined($_ = <ARGV>)) {
chomp $_;
print "'${_}'\n";
}
-e syntax OK