0

I found that we can use

perl -wnle "/RE/ and print"

such as

perl -wnle "/^.{0,80}$/ and print"

or

grep -ri someText * | perl -wnle "/^.{0,80}$/ and print"

to exclude lines longer than 80 characters.

But how do you make it into an alias in Bash?

I tried:

alias pgrep='perl -wnle "/$1/ and print"'

and then

grep -ri someText * | pgrep "^.{0,80}$"

but it'd say

Can't open ^.{0,80}$: No such file or directory.
nonopolarity
  • 3,069

2 Answers2

5

alias in bash are not designed to take arguments and won't know to what to do with it if provided with one. They should generally be avoided and should only be used for really simple command name alternatives.

Recommend using a function instead. Note that pgrep is a valid Linux binary which shouldn't be used, recommend using a non ambiguous name instead.

perlgrep() {
    perl -wnle "/$1/ and print"
} 

and now call it as

perlgrep '^.{0,80}$'

The reason for your error though is, since alias couldn't have handed the argument itself, when the expansion happened, the command became like this

grep -ri someText * | perl -wnle "/$1/ and print" '^.{0,80}$'

which is incorrect, because perl thinks '^.{0,80}$ as a filename that it needs to open and run the regex on.

Inian
  • 12,807
  • that's because alias echo2='echo $1' worked... but many people have advised against using alias... I guess when arguments are used? just that echo2 worked can Perl grep be made working if using an alias? – nonopolarity Feb 04 '20 at 08:03
  • @nopole: I'm not sure of a case how that echo2 alias could have worked. Only way I can think of is, in that case $1 would have already been set in the function/script you are invoking this alias on but echo2 foobar could never work – Inian Feb 04 '20 at 08:05
  • @nopole Try set -- foo and then run your alias. – muru Feb 04 '20 at 08:10
  • @Inian actually echo2 works on a Mac – nonopolarity Feb 04 '20 at 08:18
  • @muru thanks... somehow on my Mac alias pg='perl -wnle "/$1/ and print"' ; set -- foo ; grep -ri someText * | pg "^.{0,80}$" doesn't work... what is set -- foo btw... why foo? – nonopolarity Feb 04 '20 at 08:21
  • @nopole I meant alias echo2='echo $1' then set -- foo and echo2 bar to see how your echo2 alias really worked. – muru Feb 04 '20 at 08:28
  • @muru then it would print out foo bar... I wonder how that happened? – nonopolarity Feb 04 '20 at 08:31
  • @nopole: Are you still not accepting the fact, that aliases don't take arguments? See BashFAQ/080 - How can I make an alias that takes an argument? – Inian Feb 04 '20 at 18:23
  • 1
    @Inian aliases can take arguments. I have this in my ~/.bashrc: alias c='_c=$(fc -nl -0); bc -l <<<${_c#*c} #'. This allows me to type commands like c 7 * 8 => 56 without bothering that * will be expanded (In fact, I use something else than bc that can handle units and parse 8R2 as 8.2 Ohm, but let's keep it simple ;-)). –  Feb 04 '20 at 21:55
  • @iruvar: Yes it did look odd. Changed it now – Inian Feb 05 '20 at 03:37
1

Note that pcregrep (from the PCRE library) and GNU grep -P (when built with PCRE support) can take perl-like regular expressions and grep -P will work OK on UTF-8 data when in UTF-8 locales.

If you wanted to use perl instead, you could define a script or function to do so. Aliases won't do as aliases are just aliases, just meant to replace one string with another.

You could do:

perlgrep() (
  export RE="${1?}"; shift
  exec perl -Mopen=locale -Twnle '
    BEGIN {$ret = 1; $several_files = @ARGV > 1}
    if (/$ENV{RE}/) {
      $ret = 0;
      print $several_files ? "$ARGV: $_" : $_
    }
    END {exit $ret}' -- "$@"
)

But beware of the implications of running perl -n on arbitrary file names which are only partly mitigated by the -T option above.

Also, with -Mopen=locale, we're decoding the input and encoding the output as per the locale's charset, but file names themselves will be encoded but not decoded, which means that if filenames have byte values above 127, that won't work properly unless the locale's charset is iso8859-1.

In the end, you just need to decode the lines of input for matching only. You don't need to reencode it, not to decode/encode the file names.

So, instead, with recent versions of perl, you could do:

#! /usr/bin/perl --
use warnings;
use strict;
use Encode::Locale;
use Encode;

my $re = shift @ARGV;
my $several_files = @ARGV > 1;
my $ret = 1;

while (<<>>) {
  if (decode(locale => $_) =~ $re) {
    $ret = 0;
    print $several_files ? "$ARGV: $_" : $_
  }
}
exit $ret;

To prevent arbitrary code injection from the arguments, regexp operators like (?{code}), (??{code}) are disabled. If you want them back, you can add a use re 'eval'; towards the top of that script.

  • is it available on the Mac? my grep on Mac doesn't have a -P option... and I checked egrep and fgrep and they don't have it either – nonopolarity Feb 04 '20 at 23:27
  • @nopole, on macOS, I believe there are a number of opensource package managers (homebrew, fink...) that you can use to install opensource utilities like GNU grep. macOS tools are based on FreeBSD, and FreeBSD grep is still based on GNU grep (albeit a very old version, and one without -P support), but IIRC macOS eventually replaced it with their own possibly so they don't have to distribute its source code. pcregrep itself comes with the pcre (perl-compatible regular expression) library. – Stéphane Chazelas Feb 05 '20 at 07:04