「カット」を使用して最後のフィールドを見つける方法


310

せずに使用しsedたりawk唯一の cutフィールドの数は、すべての行で、未知または変更されたときに、どのように私は最後のフィールドを得るのですか?


8
cutコマンドに夢中ですか:)?なぜ他のLinuxコマンドがないのですか?
Jayesh Bhoi 14年

7
なしsedawkperl -pe 's/^.+\s+([^\s]+)$/$1/'
ヨルダン2014年


4
@MestreLion多くの場合、人々は問題のバリエーションに対する解決策を見つけるために質問を読みます。これは、cutサポートしていないものをサポートするという誤った前提から始まります。しかし、読者が従うのが簡単なコードを検討する必要があるという点で、私はそれが有用だと思いました。私は使用への迅速、簡単な方法を望んでいたcutため、複数の構文を使用する必要がなくawkgrepsed、などrevのことはトリックをしました。非常にエレガントで、私が検討したことのないものです(他の状況では不格好だとしても)。他の回答から他のアプローチを読むのも好きでした。
Beejor

3
ここで現実の問題が発生しました:.gitattributesファイルを更新するために、ソースツリーでさまざまなファイル拡張子をすべて見つけたいと思います。find | cut -d. -f<last>自然な傾向もそうです
studog

回答:


680

あなたはこのようなことを試すことができます:

echo 'maps.google.com' | rev | cut -d'.' -f 1 | rev

説明

  • rev 「maps.google.com」を逆にして moc.elgoog.spam
  • cut 区切り文字としてドット(つまり「。」)を使用し、最初のフィールドを選択します。 moc
  • 最後に、私たちはそれを再び逆にする com

6
使用しているだけではcutなく、sedまたawkはなしです。だから、OPはどう思いますか?
Jayesh Bhoi 14年

7
@tom OPはこの数時間でこれ以上の質問をしました。OPとのやり取りに基づいて、awk / sed / etcなどがわかります。彼の宿題では許可されていませんが、revへの言及は行われていません。だから、一撃の価値がありました
zedfoxus 14年

4
@zfusなるほど。rev後でもう一枚貼りたいと思うかもしれません。
トム

17
二重のrev大きな理想!
フォード郭

6
素晴らしい、シンプル、完璧、説明にも感謝-パイプコマンドの長いチェーンの各ステップを説明する人が足りません
Pete

128

パラメータ展開を使用します。これは、含まれている外部コマンドcut(またはgrep)の種類よりもはるかに効率的です。

data=foo,bar,baz,qux
last=${data##*,}

bashでのネイティブ文字列操作の概要については、BashFAQ#100を参照してください。


3
@ErwinWessels:bashは本当に遅いから。データを一括処理するのではなく、bashを使用してパイプラインを実行します。つまり、シェル変数にすでに1行のテキストがある場合、またはwhile IFS= read -ra array_var; do :;done <(cmd)数行を処理する場合に最適です。ただし、大きなファイルの場合、おそらくrev | cut | revの方が高速です。(もちろん、awkはそれよりも速くなります。)
Peter Cordes

2
@ PeterCordes、awkは大きなファイルの方が高速ですが、定数要素の起動コストを克服するにはかなりの入力が必要です。(シェルも存在します-ksh93のような-awkに近いパフォーマンスがあり、この回答で与えられた構文は有効なままです; bashは例外的に遅いですが、利用可能な唯一のオプションにさえ近づいていません)。
Charles Duffy、2015

1
@PeterCordesに感謝します。いつものように、各ツールにはそのユースケースがあると思います。
Erwin Wessels、2015

1
これは、bashスクリプト内の1つの変数を削減する最も高速で簡潔な方法です(既にbashスクリプトを使用している場合)。外部から呼び出す必要はありません。
Ken Sharp

1
@Balmipour、...しかし、rev であるあなたがしていることを利用してどんなOSに固有のそれを提供-それは、すべてのUNIXシステム間で標準化されていません。コマンドとユーティリティに関するPOSIXセクション章リストを参照してください-ありません。そして実際にはbash固有で${var##prefix_pattern}はありません。それは中だPOSIX shの標準、そうとは異なり、セクション2.6.2(リンク)の末尾を参照してくださいrev、それがどの互換シェル上で常に利用可能です。
Charles Duffy

89

だけではできませんcut。これが使用方法grepです:

grep -o '[^,]*$'

他の区切り文字のコンマを置き換えます。


3
逆の操作を行い、そして最後のフィールド以外のすべてを見つける行うには:grep -o '^.*,'
アリエル

2
rev私の場合はマルチバイトのユニコード文字を追加するため、これは特に便利でした。
Brice、

3
私はこれをMinGWで実行しようとしましたが、私のgrepバージョンは-oをサポートしていないためsed 's/^.*,//'、最後のコンマまでのすべての文字を空の文字列に置き換えるものを使用しました。
TamaMcGlinn

46

awkがなければ?...しかし、awkを使うととても簡単です。

echo 'maps.google.com' | awk -F. '{print $NF}'

AWKは、ポケットに入れられる強力なツールです。-Fフィールドセパレータの場合、NFはフィールド数です(最後のフィールドのインデックスも表します)。


2
これは普遍的であり、毎回期待どおりに機能します。このシナリオでは、を使用cutしてOPの最終出力を達成することは、スプーンを使用してステーキを「カット」することと似ています(意図的にしゃれた:))。awkステーキナイフです。
Hickory420 2018年

3
不要な使用を避け、echoを使用して長いファイルのスクリプトを遅くする可能性がありますawk -F. '{print $NF}' <<< 'maps.google.com'
Anil_M 2018年

14

複数の方法があります。これも使えます。

echo "Your string here"| tr ' ' '\n' | tail -n1
> here

明らかに、trコマンドの空白スペース入力は、必要な区切り文字に置き換える必要があります。


ありがとうございました!busybox sh 1.0.0で機能するもの:)
kevinf

1
これは私にとって最も単純な答えのように感じられ、パイプが少なく、より明確な意味があります
joeButler

1
これはファイル全体では機能しません。これは、OPがおそらく意味していたことです。
アミール

7

これは、カットのみを使用するための可能な唯一のソリューションです。

エコー "文字列" | カット-d '。' -f2- [repeat_following_part_forever_or_until_out_of_memory:] | カット-d '。' -f2-

このソリューションを使用すると、フィールドの数は実際には不明であり、時々変化する可能性があります。ただし、行の長さがLINE_MAX文字または改行文字を含むフィールドを超えてはならないため、このソリューションの実際の条件として、任意の数のフィールドを含めることはできません。

はい、非常にばかげた解決策ですが、私が考える基準を満たす唯一の解決策です。


2
いいね。最後の「。」をとってください。「文字列」のオフとこれは動作します。
マット

2
誰もが何かは不可能だと言ってから、誰かが実用的な答えで呼びかけてくるのが大好きです。本当にばかげているとしても。
Beejor

cut -f2-出力が変化しなくなるまでループで繰り返すことができます。
loa_in_

4

入力文字列にスラッシュが含まれていない場合は、次を使用できます basename、サブシェルを。

$ basename "$(echo 'maps.google.com' | tr '.' '/')"

これは使わない sedまたはawk使用しないcutその文言が通り、それは質問への答えとしての資格ならば、私はかなりよく分からないので、どちらか。

スラッシュを含む可能性のある入力文字列を処理する場合、これはうまく機能しません。この状況の回避策は、スラッシュを有効な入力文字列の一部ではないことがわかっている他の文字に置き換えることです。たとえば|、ファイル名にはパイプ()文字も使用できないため、次のように機能します。

$ basename "$(echo 'maps.google.com/some/url/things' | tr '/' '|' | tr '.' '/')" | tr '|' '/'


0

次のようなリストパスであるfilelist.txtという名前のファイルがある場合:c:/dir1/dir2/file1.h c:/dir1/dir2/dir3/file2.h

次に、これを行うことができます:rev filelist.txt | カット-d "/" -f1 | 回転


0

ただの楽しみのために、この古い質問にアプローチを追加します。

$ cat input.file # file containing input that needs to be processed
a;b;c;d;e
1;2;3;4;5
no delimiter here
124;adsf;15454
foo;bar;is;null;info

$ cat tmp.sh # showing off the script to do the job
#!/bin/bash
delim=';'
while read -r line; do  
    while [[ "$line" =~ "$delim" ]]; do
        line=$(cut -d"$delim" -f 2- <<<"$line")
    done
    echo "$line"
done < input.file

$ ./tmp.sh # output of above script/processed input file
e
5
no delimiter here
15454
info

bashの他に、カットのみが使用されます。ええと、エコー、私は推測します。


えっと、カットを完全に削除してbashだけを使用しないでください... x] while read -r line; do echo ${line/*;}; done <input.fileでも同じ結果が得られます。
Kaffe Myers、

-1

末尾の区切り文字が存在することを確認するだけで機能することに気付きました。したがって、私の場合、コンマと空白の区切り文字があります。最後にスペースを追加します。

$ ans="a, b"
$ ans+=" "; echo ${ans} | tr ',' ' ' | tr -s ' ' | cut -d' ' -f2
b

また、「フィールド数が不明であるか、行ごとに変化する」の要件を満たさないをans="a, b, c"生成bします。
jww
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.