简体   繁体   中英

Why is '$_' the same as $ARGV in a Perl one-liner?

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"' 'chomp; print "'$_'\\n"' results in a string that's the concatenation of

  1. chomp; print " chomp; print " (the first sequence inside single quotes),
  2. the value of its variable $_ , and
  3. \\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"

Note that the above mechanism shouldn't be used to pass values to a Perl one-liner. You shouldn't be generating Perl code from the shell. See How can I process options using Perl in -n or -p mode? for how to provide arguments to a one-liner.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM