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

后端 未结 3 824
一向
一向 2021-01-21 08:15

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 t

相关标签:
3条回答
  • 2021-01-21 08:39

    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
    
    0 讨论(0)
  • 2021-01-21 08:50

    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
    
    0 讨论(0)
  • 2021-01-21 08:52

    To your shell, 'chomp; print "'$_'\n"' results in a string that's the concatenation of

    1. 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.

    0 讨论(0)
提交回复
热议问题