最後の列のみを印刷する方法は?


25
echo -e 'one two three\nfour five six\nseven eight nine'
one two three
four five six
seven eight nine

この出力を取得するために「マジック」を行うにはどうすればよいですか?

three
six
nine

更新:この特定の方法では必要ありません。行内の列数に関係なく、一般的な解決策が必要です。たとえば、awkは常に最後の列を表示します。


2
ランスは尋ねる前に質問を調査してください。投稿の件名をGoogleで検索すると、スニペットに回答が表示されます。「awk last column」を検索すると、結果1から始まるいくつかの優れた答えが得られます。また、この5分間のawk入門書は最後まで読む価値があるため、将来何が可能かを知ることができます。
カレブ

回答:


6

'bash'、なし'sed''awk'またはでのみ実行できます'perl'

echo -e 'one two three\nfour five six\nseven eight nine' |
  while IFS=" " read -r -a line; do
    nb=${#line[@]}
    echo ${line[$((nb - 1))]}
  done

うーん、または、入力が実際にスペースで区切られていると仮定すると:... | while read -r line; do echo ${line##* }; done
グレンジャックマン

@glenn:これは私の最初のアイデアでしたが、「読み取り」マニュアルを読んだときに、この配列関数が有用だとわかりました。また、フィールドを右からインデックス付けするように簡単に変更できます。
jfg956

2
bash配列インデックスは算術評価の対象echo ${line[nb - 1]}なので、十分です。について言えばbash、「nb」をスキップできますecho ${line[-1]}。後者のよりポータブルな代替:echo ${line[@]: -1]}。(他の場所のネガティブインデックスに関するStephane Chazelasのコメントを参照してください。)
manatwork

53

試してください:

echo -e 'one two three\nfour five six\nseven eight nine' | awk '{print $NF}'


awkは99個のフィールドに制限されていることに注意してください...:/これは過去数日間に私を噛みました(ps -ef | awk '{ print $NF }'一部の行が切り捨てられていました...)Perlにはその制限がありません。(gnu.org/software/autoconf/manual/autoconf-2.67/html_node/…:「従来のAwkのレコードには99フィールドの制限があります。Tru64などの一部のAwk実装では、参照しなくても入力を分割します。この問題を回避するには、スクリプトの任意のフィールドに「FS」を通常とは異なる文字に設定し、分割を使用します。」)
オリビエデュラック14年

@OlivierDulacどのawk実装にその制限がありますか?私はそれを見たことがありません。私はmawkのどに詰まらされます32768が、私gawkigawk喜んで何百万人に対処することができます。私のbusyboxでもawk数百万を処理できます。結局awk、100のフィールドを処理できないものに出くわしたことはありません。情報がまだ関連していると確信していますか?Solaris上でも?
テルドン

@terdon、私のコメントのリンクを参照してください^^(そして、いくつかの「レガシー」システムは環境によってはloooooooog時間を生き残ることができます。 builtins(たとえば$ BASH_SOURCEなど)、NF> 99でのawkチョークなど... :()
オリビエデュラック

@OlivierDulacまあまあ。私はそれに出くわしていません。99はごく少数であるため、今日ではほとんどないことを願っています。
テルドン

14

思っているより簡単です。

$ echo one two three | awk '{print $NF}'
three

11

試してみてくださいgrep(より短く/簡単ですがawk、正規表現の使用よりも3倍遅い):

grep -o '\S\+$' <(echo -e '... seven eight nine')

またはex(さらに遅くなりますが、終了後にバッファ全体を印刷します。インプレースでソートまたは編集する必要がある場合により便利です):

ex -s +'%s/^.*\s//g' -c'%p|q!' <(echo -e '... seven eight nine')
ex +'%norm $Bd0' -sc'%p|q!' infile

その場で変更するには、交換してください-sc'%p|q!'-scwq

またはbash

while read line; do arr=($line); echo ${arr[-1]}; done < someinput

性能

生成された1GBファイルを次のように指定します:

$ hexdump -C /dev/urandom | rev | head -c1G | pv > datafile

解析時間の統計を実行しました(3倍まで実行され、MBP OS Xでテストされ、最低の結果が得られました)。

  • を使用してawk

    $ time awk '{print $NF}' datafile > /dev/null
    real    0m12.124s
    user    0m10.704s
    sys 0m0.709s
  • を使用してgrep

    $ time grep -o '\S\+$' datafile > /dev/null
    real    0m36.731s
    user    0m36.244s
    sys 0m0.401s
    
    $ time grep -o '\S*$' datafile > /dev/null
    real    0m40.865s
    user    0m39.756s
    sys 0m0.415s
  • を使用してperl

    $ time perl -lane 'print $F[-1]' datafile > /dev/null
    real    0m48.292s
    user    0m47.601s
    sys 0m0.396s
  • rev+ を使用cut

    $ time (rev|cut -d' ' -f1|rev) < datafile > /dev/null
    $ time rev datafile | cut -d' ' -f1 | rev > /dev/null
    real    1m10.342s
    user    1m19.940s
    sys 0m1.263s
  • を使用してex

    $ time ex +'%norm $Bd0_' -sc'%p|q!' datafile > /dev/null
    real    3m47.332s
    user    3m42.037s
    sys 0m2.617s
    $ time ex +'%norm $Bd0' -sc'%p|q!' datafile > /dev/null
    real    4m1.527s
    user    3m44.219s
    sys 0m6.164s
    $ time ex +'%s/^.*\s//g' -sc'%p|q!' datafile > /dev/null
    real    4m16.717s
    user    4m5.334s
    sys 0m5.076s
  • を使用してbash

    $ time while read line; do arr=($line); echo ${arr[-1]}; done < datafile > /dev/null
    real    9m42.807s
    user    8m12.553s
    sys 1m1.955s

6
... | perl -lane 'print $F[-1]'

キーポイント:-a、フィールドを@F配列に自動分割します。-lチョップオフ$/(入力レコードセパレータ)とに保存$\(出力レコードセパレータ)。で提供される8進数がないため-l、オリジナル$/は印刷(行末)に適用されます。-nループコード; -eすぐにコードを実行します。をご覧くださいman perlrun
ジョナサンKomar

5

以下を使用して行うこともできます'sed'

echo -e 'one two three\nfour five six\nseven eight nine' | sed -e 's/^.* \([^ ]*\)$/\1/'

更新:

またはもっと簡単に:

echo -e 'one two three\nfour five six\nseven eight nine' | sed -e 's/^.* //'

より単純にした方が良い!
mdpc

@mdpc:同意しますが、より複雑なソリューションがすでに投稿されているため、それを維持することにしました。
jfg956

5

または使用cut

echo -e 'one two three\nfour five six\nseven eight nine' | cut -f 3 -d' '

ただし、これは「一般的な解決策」の要件を満たしていません。rev2回使用すると、これも解決できます。

echo -e 'one two three\nfour five six\nseven eight nine' | rev | cut -f 1 -d' ' | rev

「rev」はすべてのUnix(AIX、Solarisなど)にあるとは思わないか、すべてのLinuxにインストールされているとは思いませんが、代わりのソリューションです。
jfg956

1
二重回転の場合は+1ですが、副次的な注意事項として、rev「ワイド」文字では機能せず、私の知る限り1バイト文字のみです。
マルチン

2

を使用awkすると、最初に少なくとも1つの列があるかどうかを確認できます。

echo | awk '{if (NF >= 1) print $NF}'

echo 1 2 3 | awk '{if (NF >= 1) print $NF}'

3
またはより冗長にawk 'NF{print $NF}'
マナトワーク

1

perlでは、これは次のように実行できます。

#!/usr/bin/perl

#create a line of arbitrary data
$line = "1 2 3 4 5";

# splt the line into an array (we call the array 'array', for lolz)
@array = split(' ', $line);

# print the last element in the array, followed by a newline character;
print "$array[-1]\n";

出力:

$ perl last.pl
5
$

ファイルをループすることもできます。これは、budget.datというファイルを解析するために作成したスクリプトの例です。

budget.datのサンプルデータ:

Rent              500
Food              250
Car               300
Tax               100
Car Tax           120
Mag Subscription  15

(列2のみではなく、「最後の」列のみをキャプチャする必要があることがわかります)

スクリプト:

#!/usr/bin/perl
$budgetfile = "budget.dat";
open($bf, $budgetfile)
        or die "Could not open filename: $filename $!";


print "-" x 50, "\n";
while ( $row = <$bf> ) {
        chomp $row;
        @r = split (' ', $row);
        print "$row ";
        $subtotal += $r[-1];
        print "\t$subtotal\n";
}
print "-" x 50, "\n";
print "\t\t\t Total:\t$subtotal\n\n";

私は他の誰かがすでに同じコメントをしていることに気付きました。それについてすみません、少なくとも私にもいくつかの例があります。
urbansumo
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.