シェルで文字列を分割して最後のフィールドを取得する方法


293

文字列が1:2:3:4:5あり、その最後のフィールド(5この場合)を取得したいとします。Bashを使用してどうすればよいですか?を試しましたcutが、最後のフィールドをで指定する方法がわかりません-f

回答:


430

文字列演算子を使用できます。

$ foo=1:2:3:4:5
$ echo ${foo##*:}
5

これにより、前から「:」まですべてが貪欲にトリミングされます。

${foo  <-- from variable foo
  ##   <-- greedy front trim
  *    <-- matches anything
  :    <-- until the last ':'
 }

7
これは所定の問題に対して機能しますが、文字列が(文字列演算子を使用すると空の結果が返される)場合は、以下のWilliamの答え(stackoverflow.com/a/3163857/520162)も返されます。これは、終了文字を含む(または含まない)パスを解析する場合に特に便利です。51:2:3:4:5:/
eckes 2013年

9
次に、これの反対をどのように行いますか?「1:2:3:4:」をエコーアウトするには?
Dobz 14年

12
そして、最後のセパレータの前に部品をどのように保持しますか?どうやら${foo%:*}#- 初めから; %-終わりから。#%-最短一致。##%%-最長一致。
ミハイダニーラ2014

1
パスから最後の要素を取得したい場合、どのように使用すればよいですか?echo ${pwd##*/}動作しません。
Putnik 2016

2
@Putnikそのコマンドはpwd変数と見なします。お試しくださいdir=$(pwd); echo ${dir##*/}。私のために働く!
スタンストラム2017

344

別の方法は、前後を逆にすることcutです:

$ echo ab:cd:ef | rev | cut -d: -f1 | rev
ef

これにより、最後の1つ以外のフィールド、または末尾から番号が付けられたフィールドの範囲を非常に簡単に取得できます。


17
この回答は、作者が(おそらく)すでに知っている 'cut'を使用しているため、すばらしい回答です。プラス、私はこの答えのようなので、私は「カット」を使用していますので、検索経由でこのスレッドを見つけ、この正確な質問を持っていました。
Dannid 2013年

6
スペースを区切り文字として使用する人々のためのカットアンドペーストecho "1 2 3 4" | rev | cut -d " " -f1 | rev
フード

2
回転| カット-d -f1 | 回転はとても賢いです!ありがとう!たくさん助けてくれました(私のユースケースはrev | -d '' -f 2- | rev
EdgeCaseBerg

1
私はいつも忘れてしまいましたrevcut -b20- | rev | cut -b10- | rev
Shern89

私はこの解決策に終わりました。 "awk -F" / "'{print $ NF}'"でファイルパスを切り取ろうとすると、空白を含むファイル名も切り取られるため、やや壊れました
THX

76

カットを使用して最後のフィールドを取得することは困難ですが、awkおよびperlでのいくつかの解決策を以下に示します

echo 1:2:3:4:5 | awk -F: '{print $NF}'
echo 1:2:3:4:5 | perl -F: -wane 'print $F[-1]'

5
受け入れ答えを超えるこのソリューションの大きな利点:それも含まれていたり、仕上げ含まれていないパスと一致する/:文字/a/b/c/d/a/b/c/d/(同じ結果をもたらすd処理するときに)pwd | awk -F/ '{print $NF}'。承認された回答の場合、空の結果になります/a/b/c/d/
eckes

@eckes AWKソリューションの場合、GNU bashでは、バージョン4.3.48(1)-releaseは正しくありません。末尾のスラッシュがあるかどうかに関係なく、重要です。単にAWKを/区切り文字として使用します。パスが/my/path/dir/最後の区切り文字の後に値を使用する場合、これは単に空の文字列です。ですから、私のようにそうする必要がある場合は、スラッシュの末尾を避けるのが最善です。
スタムスター

最後のフィールドまでの部分文字列を取得するにはどうすればよいですか?
blackjacx

1
@blackjacxいくつかの癖がありますが、のようなものawk '{$NF=""; print $0}' FS=: OFS=:が十分に機能することがよくあります。
ウィリアムパーセル

29

かなり単純な使用法(たとえば、区切り文字のエスケープなし)を想定すると、grepを使用できます。

$ echo "1:2:3:4:5" | grep -oE "[^:]+$"
5

内訳-行末($)の区切り文字([^:])以外のすべての文字を検索します。-oは、一致する部分のみを印刷します。


-Eは拡張構文の使用を意味します。[^ ...]は、リストされた文字以外のものを意味します。+ 1つ以上の該当するヒット(パターンの可能な最大長になります。このアイテムはGNU拡張です)-たとえば、分離文字はコロンです。
Alexander Stohr

18

一方通行:

var1="1:2:3:4:5"
var2=${var1##*:}

別の、配列を使用して:

var1="1:2:3:4:5"
saveIFS=$IFS
IFS=":"
var2=($var1)
IFS=$saveIFS
var2=${var2[@]: -1}

さらに別の配列:

var1="1:2:3:4:5"
saveIFS=$IFS
IFS=":"
var2=($var1)
IFS=$saveIFS
count=${#var2[@]}
var2=${var2[$count-1]}

Bash(バージョン> = 3.2)正規表現の使用:

var1="1:2:3:4:5"
[[ $var1 =~ :([^:]*)$ ]]
var2=${BASH_REMATCH[1]}

11
$ echo "a b c d e" | tr ' ' '\n' | tail -1
e

区切り文字を改行に変換し、で最後のエントリを選択するだけtail -1です。


最後の項目にが含まれていると失敗します\nが、ほとんどの場合、最も読みやすいソリューションです。
Yajo 14

7

使用sed

$ echo '1:2:3:4:5' | sed 's/.*://' # => 5

$ echo '' | sed 's/.*://' # => (empty)

$ echo ':' | sed 's/.*://' # => (empty)
$ echo ':b' | sed 's/.*://' # => b
$ echo '::c' | sed 's/.*://' # => c

$ echo 'a' | sed 's/.*://' # => a
$ echo 'a:' | sed 's/.*://' # => (empty)
$ echo 'a:b' | sed 's/.*://' # => b
$ echo 'a::c' | sed 's/.*://' # => c

4

最後のフィールドが単一の文字である場合、これを行うことができます:

a="1:2:3:4:5"

echo ${a: -1}
echo ${a:(-1)}

bashで文字列操作を確認してください。


これは機能しません。これは、最後のフィールドではなく、の最後の文字を示しますa
gniourf_gniourf 2013年

1
確かに、それがアイデアです。最後のフィールドの長さがわかっている場合は、それが適切です。そうでない場合は、何か他のものを使用する必要があります...
Ab Irato 2013年

4

ここには多くの良い答えがありますが、それでもbasenameを使用してこれを共有したいと思います:

 basename $(echo "a:b:c:d:e" | tr ':' '/')

ただし、文字列にすでにいくつかの「/」がある場合は失敗します。スラッシュ/が区切り文字の場合は、ベース名を使用する必要があります(使用する必要があります)。

これは最良の答えではありませんが、bashコマンドを使用してクリエイティブにできる方法を示しているだけです。


2

Bashを使用する。

$ var1="1:2:3:4:0"
$ IFS=":"
$ set -- $var1
$ eval echo  \$${#}
0

echo ${!#}代わりに使用できましたeval echo \$${#}
Rafa

2
echo "a:b:c:d:e"|xargs -d : -n1|tail -1

最初にxargsを使用すると、 ":"を使用して分割されます。-n1は、すべての行に1つの部分しかないことを意味します。次に、最後の部分を削除します。


1
for x in `echo $str | tr ";" "\n"`; do echo $x; done

2
いずれかのフィールドに空白がある場合、これは問題になります。また、最後のフィールドを取得する問題については直接触れていません。
chepner

1

Pythonに慣れている人にとっては、https://github.com/Russell91/pythonpyがこの問題を解決するための良い選択です。

$ echo "a:b:c:d:e" | py -x 'x.split(":")[-1]'

pythonpyヘルプから:-x treat each row of stdin as x

このツールを使用すると、入力に適用されるPythonコードを簡単に作成できます。


1

簡単な解決策は入力文字列の順序を逆にすることですが、答えが少し遅れる可能性があります。これにより、長さに関係なく常に最後のアイテムを取得できます。

[chris@desktop bin]$ echo 1:2:3:4:5 | rev | cut -d: -f1
5

ただし、この方法を使用していて、数値が1桁よりも大きい(または状況によっては1文字よりも大きい)場合は、パイプ処理された出力に対して別の 'rev'コマンドを実行する必要があります。

[chris@desktop bin]$ echo 1:2:3:4:5:8:24 | rev | cut -d: -f1
42
[chris@desktop bin]$ echo 1:2:3:4:5:8:24 | rev | cut -d: -f1 | rev
24

応援よろしくお願いします


1

組み込みの読み取りを使用するソリューション:

IFS=':' read -a fields <<< "1:2:3:4:5"
echo "${fields[4]}"

または、より一般的にするには:

echo "${fields[-1]}" # prints the last item

0

Pythonが好きで、パッケージをインストールするオプションがある場合は、このPythonユーティリティを使用できます

# install pythonp
pythonp -m pip install pythonp

echo "1:2:3:4:5" | pythonp "l.split(':')[-1]"
5

pythonはこれを直接実行できます echo "1:2:3:4:5" | python -c "import sys; print(list(sys.stdin)[0].split(':')[-1])"
。– MortenB

@MortenBあなたは間違っています。pythonpパッケージの全体的な目的はpython -c、少ない文字タイプで同じことを行うことです。リポジトリのREADMEをご覧ください。
爆弾

0

の正規表現一致sedは貪欲です(常に最後の出現に移動します)。これは、ここで有利に使用できます。

$ foo=1:2:3:4:5
$ echo ${foo} | sed "s/.*://"
5
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.