【问题标题】:Shell script containing a Perl one-liner has blank results包含 Perl 单行的 Shell 脚本的结果为空白
【发布时间】:2012-05-28 02:37:37
【问题描述】:

我有一个在命令行上运行良好的 Perl 单行代码:

perl -nle 'm"\w+:x:\d+:\d+:\S+:/S+:(\S+)$" and $h{$1}++; END{ print "$_: $h{$_}" foreach sort { $h{$b} <=> $h{$a} } keys %h }' /etc/textfile

我已将其放入名为 shell.sh 的 shell 文件中,这样下一个人就不必复制/粘贴,只需运行它即可:

#!/bin/sh
perl -nle 'm"\w+:x:\d+:\d+:\S+:/S+:(\S+)$" and $h{$1}++; END{ print "$_: $h{$_}" foreach sort { $h{$b} <=> $h{$a} } keys %h }' /etc/textfile

我尝试在命令行上运行它并没有得到任何结果;它只是加载一个没有输出的新提示。有人看到我做错了吗?

以下是一些系统规格:

Linux 版本 2.6.32-220.13.1.el6.x86_64

(gcc 版本 4.4.6 20110731 (Red Hat 4.4.6-3) (GCC)

GNU bash,版本 4.1.2(1)-release (x86_64-redhat-linux-gnu)

以下是文本文件中的一些内容:

rfink:x:140:140:rat fink:/var/lib/rfink:/sbin/nologin                                 
edible:x:16252:10001:eric idle:/users/eidle/:/bin/bash                                       
tsawyer:x:30855:10001:tom sawyer:/users/tsawyer/:/bin/bash                                
karthur:x:30886:10001:King Arthur:/users/karthur/:/bin/bash                                         
karthur:x:30886:10001:king arthur:/users/karthur/:/bin/bash                                         
jcash:x:30887:10001:john cash:/users/jcash/:/bin/bash                              
hpotter:x:30887:10001:harry potter:/users/hpotter/:/bin/bash                              
triddle:x:30956:10001:tom riddle:/users/triddle/:/bin/bash 

【问题讨论】:

  • 你能从文本文件中展示一个样本吗?
  • 已添加。我很确定正则表达式是正确的,因为单行从命令行工作。关于 shell 脚本的某些东西似乎搞砸了。
  • /etc/textfile 中的问题示例中的所有尾随空格是否逐字逐句?
  • 最后一行说明了这一点。每行有 1 个尾随空格(所有其他行上的多个空格都发生在 c/p 中)。
  • 这无济于事,但为什么不将 perl 单行代码转换为 perl 脚本而不是 bash 脚本呢?

标签: regex linux perl command-line


【解决方案1】:

快速解答

perl -nle 'm"\w+:x:\d+:\d+:[^:]+:\S+:(\S+)\s*$" and $h{$1}++;
  END{ print "$_: $h{$_}" foreach sort { $h{$b} <=> $h{$a} } keys %h }' \
  /etc/textfile

您的正则表达式存在三个问题。

  1. 组 ID 后面的字段可能包含空格,因此将该子模式替换为 [^:]+ 以匹配一个或多个非冒号字符。
  2. 您在子模式中使用了错误的斜杠来匹配主目录。
  3. $ 之前插入\s* 以允许每行出现可选的尾随空格。

输出:

/bin/bash: 7
/sbin/nologin: 1

其他方法

Perl 有一个 awk 模式,它允许

perl -F: -lane '++$sh{$F[-1]};
  END{print "$_: $sh{$_}" for sort { $sh{$b} <=> $sh{$a} } keys %sh}' \
  /etc/textfile

必须删除尾随空格似乎取消了语法优势。

perl -F: -lane '($sh = pop @F) =~ s/\s+$//; ++$sh{$sh};
  END{print "$_: $sh{$_}" for sort { $sh{$b} <=> $sh{$a} } keys %sh}' \
  /etc/textfile

您可以使用管道来获得最佳效果:

perl -pe 's/[^\S\n]+$//' /etc/textfile |
  perl -F: -lane 'print $F[-1]' |
    sort | uniq -c | sort -nr

输出转置列,但您得到相同的信息。

注意在管道的第一个命令中使用regex double-negative technique 来删除除换行符以外的所有空格。

 7 /bin/bash
      1 /sbin/nologin

作为 shell 脚本

你的问题需要一个 shell 脚本,所以——让daxim’s answer 振作起来——就是

#! /bin/sh

perl -MUser::pwent -le \
  '$_->shell && print $_->shell while $_ = getpwent' |
  sort | uniq -c | sort -nr

请注意,这不处理名为 0 的 shell 的异常情况。

如果你不一定要读取系统/etc/passwd,那么你的脚本就变成了

#! /bin/sh

if [ $# -eq 0 ]; then
  echo Usage: $0 passwd-file .. 1>&2
  exit 1
fi

perl -pe 's/[^\S\n]+$//' "$@" |
  perl -lne 'm|\w+:x:\d+:\d+:[^:]+:\S+:(\S+)$| && print $1' |
    sort | uniq -c | sort -nr

不同的系统使用不同的格式,所以我建议像上面那样确定您的期望,而不是盲目地打印最后一个字段,不管它是什么。这可能意味着应对偶尔的空输出。

【讨论】:

  • 很好,这行得通!实际上,我只是懒惰了那个 [^:]+ 条目并将冒号之间的整个短语更改为另一个 \S+。仍然不确定为什么我所拥有的单行程序会从命令行给我结果,而不是从 .sh 文件中给我结果。
  • 我很高兴它有帮助。除非您的用户的主目录是 /SSSSSSS、/SSSSS 和 /SS(或者存在复制粘贴错误),否则我看不出它们是如何生成输出的。
  • 是的,很可能是湿件的问题。 IIRC(这一直是在星期五,所以这是一个远景)我手动将代码输入到 .sh 脚本中,这样我就可以轻松地反转那个斜线并破坏整个事情。谢谢!
【解决方案2】:

当存在specialised parser 时避免使用临时正则表达式。

perl -MUser::pwent=getpwent -e'
    while (my $pwent = getpwent) { $h{ $pwent->shell }++; }
    END { print "$_: $h{$_}\n" for sort { $h{$b} <=> $h{$a} } keys %h }
'

splitindex/substrunpack 等更简单的结构可以使用时,请避免使用正则表达式。这里我利用autosplit

perl -F: -lane'
    $h{ $F[-1] }++;
    END { print "$_: $h{$_}" for sort { $h{$b} <=> $h{$a} } keys %h }
' /etc/textfile

这使得程序更短、更易读。

【讨论】:

    猜你喜欢
    • 2014-05-12
    • 2014-07-01
    • 1970-01-01
    • 2019-07-20
    • 2012-04-01
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多