3

コマンドラインで正常に動作する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ので、次の人はコピー/貼り付けする必要がなく、実行することができます。

#!/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)-リリース(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 
4

2 に答える 2

3

素早い回答

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

正規表現には 3 つの問題がありました。

  1. グループ ID の後のフィールドにはスペースが含まれている可能性があるため、そのサブパターンを[^:]+1 つ以上のコロン以外の文字と一致するように置き換えます。
  2. ホーム ディレクトリに一致させるために、サブパターンで間違ったスラッシュを使用しました。
  3. \s*前に挿入$して、各行のオプションの末尾の空白を許可します。

出力:

/ビン/バッシュ: 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 手法を使用していることに注意してください。

      7 /ビン/バッシュ
      1 /sbin/nologin

シェルスクリプトとして

あなたの質問はシェル スクリプトを要求するので、daxim の回答を無効にするために、つまり

#! /bin/sh

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

これは、 という名前のシェルの異常なケースを処理しないことに注意してください0

システム/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

異なるシステムは異なるフォーマットを使用するため、最後のフィールドが何であれ、やみくもに印刷するのではなく、上記のように期待を明確にすることをお勧めします。これは、時折空の出力に対処することを意味する場合があります。

于 2012-05-21T15:48:02.003 に答える
2

特殊なパーサーが存在する場合は、アドホックな正規表現を避けます。

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/substrなどの単純な構成で十分な場合は、reg-ex を避けてくださいunpack。ここでautosplitを利用します:

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

これにより、はるかに短く、読みやすいプログラムが作成されます。

于 2012-05-21T16:31:57.053 に答える