在不使用 ARGV 的情况下将参数从 bash 传递给 perl

pass an argument from bash to perl without use of ARGV

非常简单的问题(我认为),令我惊讶的是我似乎找不到答案。所以到目前为止我有以下内容:

£ perl -ne 'print if /ENGPacific Beach\s\s/' 15AM171H0N15000GAJK5 \
| perl -ane 'print "$F[1]|";END{print "[=11=]"}' | xargs -i -0 echo {}
    3346|10989|95459|139670|2239329|3195595|3210017|

所以....第一个管道是因为文件是 1.5G,所以最初不进行记录分离大大加快了速度。 xargs 部分是为了演示我正在尝试做什么。基本上是下面的

| xargs -i perl --setperlvar pipeContents={} -ane 'print if $F[3] =~ /$pipeContents/' 15AM171H0N15000GAJK5

1) 我知道我可以在脚本中使用 ARGV。我知道整个事情应该只是一个脚本。让我们忽略那些位。我对 -n 的爱是无止境的。

2) 抱歉,我自己找不到。我敢肯定它非常明显……不过,我在 perldoc 中进行了一些挖掘,但什么也没发现。

3) 我会对 bash/zsh 解决方案感兴趣,该解决方案强制 {} 也由 perl ticks 中间的 shell 解释。

开始前的两点注意事项:

  • 模式中的尾部 | 将导致每一行都匹配。它需要被删除。
  • /3346|10989|95459|139670|2239329|3195595|3210017/ 将匹配 9993346,因此您需要锚定模式。

以下所有解决方案都修复了这些问题。


您可以通过

向程序传递数据
  • 参数列表
  • 环境
  • 一个打开的文件描述符(例如 stdin,但也可以使用 fd 3 或更高版本)到管道
  • 外部存储(文件、数据库、内存缓存守护进程等)

您仍然可以使用参数列表。您只需要在循环开始之前从 @ARGV 中删除参数,方法是使用 BEGIN 或避免使用 -n.

perl -ne'print if /ENGPacific Beach\s\s/' 15AM171H0N15000GAJK5 |
perl -ane'push @p, $F[1]; END { print join "|", @p; }' |
xargs -i perl -ane'
    BEGIN { $p = shift(@ARGV); }
    print if $F[3] =~ /^(?:$p)\z/;
' {} 15AM171H0N15000GAJK5

Perl 也有一个内置的参数解析函数,您可以使用 -s 的形式。

perl -ne'print if /ENGPacific Beach\s\s/' 15AM171H0N15000GAJK5 |
perl -ane'push @p, $F[1]; END { print join "|", @p; }' |
xargs -i perl -sane'print if $F[3] =~ /^(?:$p)\z/' -- -p={} 15AM171H0N15000GAJK5

xargs 似乎没有设置环境变量的选项,因此采用该方法有点复杂。

perl -ne'print if /ENGPacific Beach\s\s/' 15AM171H0N15000GAJK5 |
perl -ane'push @p, $F[1]; END { print join "|", @p; }' |
xargs -i sh -c '
    P="" perl -ane'\''print if $F[3] =~ /^(?:$ENV{P})\z/'\'' 15AM171H0N15000GAJK5
' dummy {}

单行涉及 xargs 很奇怪。如果我们避免 xargs,我们可以把上面的(丑陋的)命令翻过来,给出一些相当不错的东西。

P="$(
    perl -ne'print if /ENGPacific Beach\s\s/' 15AM171H0N15000GAJK5 |
    perl -ane'push @p, $F[1]; END { print join "|", @p; }'
)" perl -ane'print if $F[3] =~ /^(?:$ENV{P})\z/' 15AM171H0N15000GAJK5

顺便说一下,您不需要第二个 perl 来只拆分匹配的行。

P="$(
    perl -ne'
       push @p, (split)[1] if /ENGPacific Beach\s\s/;
       END { print join "|", @p; }
    ' 15AM171H0N15000GAJK5
)" perl -ane'print if $F[3] =~ /^(?:$ENV{P})\z/' 15AM171H0N15000GAJK5

也就是说,我认为应该避免重复使用 $ENV{P} 以加快速度。

P=... perl -ane'print if $F[3] =~ /^(?:$ENV{P})\z/o' 15AM171H0N15000GAJK5

从那里,我看到了两种可能的速度改进。 (测试确定。)

  1. 避免在最后perl.

    完全分裂
    P=... perl -ne'
       BEGIN { $re = qr/^(?:\S+\s+){3}(?:$ENV{P})\s/o; }
       print if /$re/o;
    ' 15AM171H0N15000GAJK5
    
  2. 在最后perl.

    完全避免使用正则表达式
    P=... perl -ane'
       BEGIN { %h = map { $_ => 1 } split /\|/, $ENV{P} }
       print if $h{$F[3]};
    ' 15AM171H0N15000GAJK5
    

一种方便的传递参数的方法是通过 -s switch,它可以为程序启用命令行开关

perl -s -E'say $var' -- -var=value

程序后的--标志着程序参数的开始。然后 -var 在程序中引入一个变量 $var,并在 = 之后为其提供一个值; shell 首先扩展了那里的内容。只有 -var 变量 $var 得到值 1.

任何此类选项都必须出现在可能的文件名之前,并且它们已从 @ARGV 中删除,以便程序可以正常处理提交的文件

perl -s -ne'...' -- -var="$SHELL_VAR" filename

其中 -var={} 也适用。在某些 shell 中(其中 tcsh )可能需要转义,\{\}

不过,我也觉得还是不去xargs比较好。请参阅 以了解非常全面的讨论和各种方式,以及他们在此 post 下方的评论以了解如何使用 -s.

避免它