包含Perl单线程的Shell脚本具有空白结果

我有一个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

(海湾合作委员会版本4.4.6 20110731(红帽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 

快速回答

 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 

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

请注意在管道的第一个命令中使用正则表达式的双重否定技术来移除除换行符之外的所有空白。

  7 / bin / bash
       1 / sbin / nologin 

作为一个shell脚本

你的问题要求提供一个shell脚本,以此来缓和daxim的回答 – 那就是

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

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

避免专门的分析器存在时的临时正则表达式。

 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就可以避免reg-ex。 在这里我利用autosplit :

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

这使得更短,更可读的程序。