Jay Wong
Jay Wong

Reputation: 97

How to capture single quote when using Perl in CLi?

Suppose I have a text file with content like below:

'Jack', is a boy
'Jenny', is a girl
...
...
...

I'd like to use perl in Cli to only capture the names between pairs of single quotes

cat text| perl -ne 'print $1."\n" if/\'(\w+?)\'/'

Above command was what I ran but it didn't work. It seems like "'" messed up with Shell.

I know we have other options like writing a perl script. But given my circumstances, I'd like to find a way to fulfill this in Shell command line.

Please advise.

Upvotes: 1

Views: 136

Answers (2)

doubleDown
doubleDown

Reputation: 8398

Here are some methods that do not require manually escaping the perl statement:
(Disclaimer: I'm not sure how robust these are – they haven't been tested extensively)

  1. Cat-in-the-bag technique

    perl -ne "$(cat)" text
    

    You will be prompted for input. To terminate cat, press Ctrl-D.

    One shortcoming of this: The perl statement is not reusable. This is addressed by the variation:

    $pline=$(cat)
    perl -ne "$pline" text
    


  2. The bash builtin, read

    Multiple lines:

    read -rd'^[' pline
    

    Single line:

    read -r pline
    

    Reads user input into the variable pline.

    The meaning of the switches:

    • -r: stop read from interpreting backslashes (e.g. by default read interprets \w as w)
    • -d: determines what character ends the read command.
      • ^[ is the character corresponding to Esc, you insert ^[ by pressing Ctrl-V then Esc.

  3. Heredoc and script.
    (You said no scripts, but this is quick and dirty, so might as well...)

    cat << 'EOF' > scriptonite
    print $1 . "\n" if /'(\w+)'/
    EOF
    

    then you simply

    perl -n scriptonite text
    

Upvotes: 0

amon
amon

Reputation: 57600

The shell has the interesting property of concatenating quoted strings. Or rather, '...' or "..." should not be considered strings, but modifiers for available escapes. The '...'-surrounded parts of a command have no escapes available. Outside of '...', a single quote can be passed as \'. Together with the concatenating property, we can embed a single quote like

$ perl -E'say "'\''";'
'

into the -e code. The first ' exits the no-escape zone, \' is our single quote, and ' re-enters the escapeless zone. What perl saw was

perl        // argv[0]
-Esay "'";  // argv[1]

This would make your command

cat text| perl -ne 'print $1."\n" if/'\''(\w+?)'\''/'

(quotes don't need escaping in regexes), or

cat text| perl -ne "print \$1.qq(\n) if/'(\w+?)'/"

(using double quotes to surround the command, but using qq// for double quoted strings and escaping the $ sigil to avoid shell variable interpolation).

Upvotes: 3

Related Questions