如何将 Perl grep 的一行版本变成别名?

如何将 Perl grep 的一行版本变成别名?

我发现我们可以使用

perl -wnle "/RE/ and print"

例如

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

或者

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

排除长度超过 80 个字符的行。

但是如何将其设置为 Bash 中的别名呢?

我试过:

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

进而

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

但它会说

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

答案1

aliasbash不是旨在接受参数,并且如果提供参数,则不知道如何处理它。通常应避免使用它们,并且仅应将它们用于非常简单的命令名称替代。

建议使用函数代替。请注意,这pgrep是一个有效的 Linux 二进制文件,不应使用,建议使用明确的名称。

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

现在称其为

perlgrep '^.{0,80}$'

但错误的原因是,由于alias无法传递参数本身,当扩展发生时,命令变成这样

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

这是不正确的,因为perl'^.{0,80}$其视为需要打开并运行正则表达式的文件名。

答案2

请注意pcregrep(从PCRE库)和 GNU grep -P(当使用 PCRE 支持构建时)可以采用类似 perl 的正则表达式,并且grep -P在 UTF-8 语言环境中可以正常处理 UTF-8 数据。

如果您想使用perl它,您可以定义一个脚本或函数来执行此操作。别名不行,因为别名只是别名,只是将一个字符串替换为另一个字符串。

你可以这样做:

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}' -- "$@"
)

但要小心perl -n在任意文件名上运行的影响上述选项只能部分缓解这些问题-T

另外,使用-Mopen=locale,我们将根据语言环境的字符集解码输入并编码输出,但文件名本身将是编码的但不是已解码,这意味着如果文件名的字节值高于 127,则除非语言环境的字符集为 iso8859-1,否则将无法正常工作。

最后,你只需要解码仅用于匹配的输入行。您不需要重新编码它,也不需要解码/编码文件名。

因此,使用最新版本的perl,您可以执行以下操作:

#! /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;

为了防止从参数注入任意代码,禁用了(?{code}),等正则表达式运算符。如果您希望它们回来,您可以在该脚本的顶部(??{code})添加。use re 'eval';

相关内容