文字列の最後の3文字のみを印刷するコマンド


30

cutコマンドはn文字列の最初の文字を印刷できることを知っていますが、最後のn文字を選択する方法はありますか?

文字数が可変の文字列がある場合、文字列の最後の3文字のみを印刷するにはどうすればよいですか。例えば。

必要な「無制限の」出力は「ted」です
「987654」が必要な出力は「654」です
必要な「123456789」の出力は「789」です

回答:


52

なぜ誰も明白な答えを与えていないのですか?

sed 's/.*\(...\)/\1/'

…またはやや目立たない

grep -o '...$'

確かに、2番目のものには、3文字未満の行が消えるという欠点があります。しかし、質問はこのケースの動作を明示的に定義しませんでした。


6
またはgrep -o '.\{3\}$'
Avinash Raj 14年

3
またはecho "unlimited" | python -c "print raw_input()[-3:]"
キロ14年

8
@Kiroまたは"echo unlimited" | java -jar EnterpriseWordTrimmer.jar、しかし、文字操作のためにより重い言語を持ち込むことは本当に必要だとは思わない。
wchargin 14年

11
あなたが忘れてしまった@WCharginjava -server -Xms300M -Xmx3G -XX:+UseParallelGC -cp /path/to/all/the/jars/ -Dinput.interactive=false -Dinput.pipe=true -Dconfig.file=/path/to/config/last-three-letters.cfg -jar ...
HJK

6
grep -o -P '.{0,3}$'行が3文字未満の場合でも、最後の3文字を印刷します。-P中括弧をエスケープする必要がなくなります。
ラグードッダ

43

シンプルに保つ-しっぽ

文字をカウントするためだけに、正規表現や複数のプロセスは必要ありません。ファイルの最後のを表示するためによく使用される
コマンドtailには、オプション()があります。-c--bytes

$ printf 123456789 | tail -c 3
789

(シェルを使用している場合、mikeservの答えのようなメソッドを使用するのが理にかなっていますtail

本物のユニコード文字?

ここで、最後の3 文字を要求します。それはこの答えがあなたに与えるものではありません:最後の3 バイトを出力します

各文字が1バイトである限り、tail -c機能します。したがって、文字セットがASCIIISO 8859-1またはバリアントの場合に使用できます。

一般的なUTF-8形式のようにUnicode入力がある場合、結果は間違っています。

$ printf 123αβγ | tail -c 3
�γ

この例でUTF-8は、ギリシャ文字のアルファ、ベータ、ガンマの長さは2バイトです。

$ printf 123αβγ | wc -c  
9

このオプション-mは、少なくとも実際のUnicode文字をカウントできます。

printf 123αβγ | wc -m
6

さて、最後の6バイトで最後の3文字が得られます:

$ printf 123αβγ | tail -c 6
αβγ

そのため、tail一般的な文字の処理をサポートせず、試してさえいません(以下を参照)。可変サイズの行を処理しますが、可変サイズの文字は処理しません。

このようにしましょう:tail解決する問題の構造にはちょうどいいですが、データの種類には間違っています。

GNU coreutils

さらに見ると、それはあなたのGNU coreutilsの、基本的なツールのコレクションが好きなことが判明しsedlstailcut、まだ完全に国際化されていません。これは主にUnicodeのサポートに関するものです。
たとえば、cutここではキャラクターのサポートにテールの代わりに使用するのに適した候補です。バイトまたは文字、-c--bytes)および-m--chars)を操作するオプションがあります。

バージョン-m/ 2013では、 実装されていないの--charsは/ だけです!
cut (GNU coreutils) 8.21

からinfo cut

`-c CHARACTER-LIST'
`--characters=CHARACTER-LIST'
     Select for printing only the characters in positions listed in CHARACTER-LIST.  
     The same as `-b' for now, but internationalization will change that.


参照してください、この答え缶は、UTF-8で`カット-c`を(` --characters`)を使用していませんか?


2
実際、現在のロケールがUTF-8エンコーディングを指定している限り、他の回答のほとんどはUnicodeをうまく処理しているようです。あなたとグレン・ジャックマンのcutベースのソリューションだけがそうではないようです。
イルマリカロネン14年

@IlmariKaronen本当に、ヒントをありがとう。いくつかの追加の詳細を加えて編集しました。
フォルカーシーゲル14年

1
POSIX tailは、文字ではなくバイトを処理するように明示的に指定していることに注意してください。かつてキャラクターを選択するための新しいオプションを追加するパッチを作成しましたが、マージされなかったと思います:-/
Martin Tournoij

同様に、ファイル・モードでは動作しませんtail -c3 -n10 /var/log/syslog
サンキャッチャー

@Suncatcher試したところ、うまくいきました。あなたが見る問題は何ですか?あなたのコマンドtail -c3 -n10 /var/log/syslogは最後の10行を要求します、そしてそれは私のために働きます。オプションを使用し、-c3その後、競合するオプションを使用します-n10。後者のオプションが優先されます。
フォルカーシーゲル

36

テキストがと呼ばれるシェル変数にある場合、STRINGこれをbashzshまたはmkshシェルで実行できます。

printf '%s\n' "${STRING:(-3)}"

または

printf '%s\n' "${STRING: -3}"

また、その構文が由来するksh93で作業する利点もあります。

ポイントは、:をから分離する必要があるということです。-そうしないと${var:-default}、Bourneシェルの演算子になります。

zshまたはyashシェルの同等の構文は次のとおりです。

printf '%s\n' "${STRING[-3,-1]}"

2
より多くの情報を検索できるように、どのような構文/操作と呼ばれていますか?
Tulainsコルドバ

6
Substring Expansionと呼ばれます。これは一種のパラメータ拡張です。一般的な形式は$ {parameter:offset:length}ですが、長さフィールドはオプションです(そして、ご覧のとおり、上記の回答では省略されています)。DopeGhotiは、(長さフィールドを${STRING:(-3):3}指定して)、(との間にスペースを入れて)、またはを書き込むこともできます。${STRING: -3}:-${STRING: -3:3}
G-Manは「

この場合、3「最後の文字から3番目から3番目の文字を含む」を要求するため、長さを指定することは多少意味がありません。 、包括的」。
-DopeGhoti


11

文字列が変数内にある場合、次を実行できます。

printf %s\\n "${var#"${var%???}"}"

$varlike の値から最後の3文字を削除します。

${var%???}

...そして、$varすべての頭から取り除かれますが、次のように取り除かれました:

${var#"${var%???}"}

この方法には長所と短所があります。明るい面では、完全にPOSIXに移植可能であり、最新のシェルで動作するはずです。また、もし$var少なくとも3文字の含まれていません、何もしますが、末尾\newlineが印刷されます。あなたがあればその後、再び、欲しいそれが、その場合には印刷され、次のような追加の手順が必要になります。

last3=${var#"${var%???}"}
printf %s\\n "${last3:-$var}"

その方法で$last3は、$var3バイト以下のバイトが含まれている場合にのみ空になります。そして、$varしか置換される$last3場合は$last3空であるかunset-と我々はそれがない知っているunset私達はちょうどそれを設定しているため。


それはかなり整頓された+1です。余談:printfフォーマット文字列を引用しない理由は何ですか?
jasonwryan

なぜ${VARNAME:(-3)}(仮定してbash)使用しないのですか?
DopeGhoti

1
明確にしてくれてありがとう。...少し奇妙に(私には)それが見えていても、理にかなって
jasonwryan

1
@DopeGhoti-それは私がほとんど決してしない仮定だからです。これは、bashPOSIX互換性を要求する他のシェルと同様に機能します。
mikeserv 14年

3
@odyssey-問題は、残念ながら、私がここで言及した最新のPOSIX互換シェルではcshありません。POSIXシェル仕様はの後にモデル化されており、これは従来のBourneスタイルのシェルと両方の組み合わせをモデル化したものです。の優れたジョブ制御機能と古いボーンスタイルのI / Oリダイレクトの両方が組み込まれました。また、上記で説明した文字列操作の概念など、いくつかのことも追加しました。私の知る限り、これは従来のどの方法でも機能しない可能性があります。kshcshkshcshcsh
mikeserv 14年

7

あなたはこれを行うことができますが、これは少し...過剰です:

for s in unlimited 987654 123456789; do
    rev <<< $s | cut -c 1-3 | rev
done 
ted
654
789

3

utf-8文字列の防弾ソリューション:

utf8_str=$'\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82' # привет

last_three_chars=$(perl -CAO -e 'print substr($ARGV[0], -3)' "$utf8_str")

または使用:

last_three_chars=$(perl -MEncode -CO -e '
  print substr(decode("UTF-8", $ARGV[0], Encode::FB_CROAK), -3)
' "$utf8_str")

不正な形式のデータ処理を防ぐため。

例:

perl -MEncode -CO -e '
  print substr(decode("UTF-8", $ARGV[0], Encode::FB_CROAK), -3)
' $'\xd0\xd2\xc9\xd7\xc5\xd4' # koi8-r привет

このようなものを出力します:

utf8 "\xD0" does not map to Unicode at /usr/lib/x86_64-linux-gnu/perl/5.20/Encode.pm line 175.

ロケール設定に依存しません(つまりで動作しますLC_ALL=C)。Bashsedgrepawkrevこのようなものが必要になります。LC_ALL=en_US.UTF-8

一般的な解決策:

  • 受信バイト
  • エンコードを検出する
  • バイトを文字にデコードする
  • 文字を抽出する
  • 文字をバイトにエンコード

uchardetを使用してエンコードを検出できます。関連プロジェクトも参照してください。

Perlのエンコード、Python 2.7のコーデックでデコード/エンコードできます

utf-16le文字列から最後の3文字を抽出し、これらの文字をutf-8に変換します

utf16_le_str=$'\xff\xfe\x3f\x04\x40\x04\x38\x04\x32\x04\x35\x04\x42\x04' # привет

chardet <<<"$utf16_le_str"  # outputs <stdin>: UTF-16LE with confidence 1.0

last_three_utf8_chars=$(perl -MEncode -e '
    my $chars = decode("utf-16le", $ARGV[0]);
    my $last_three_chars = substr($chars, -3);
    my $bytes = encode("utf-8", $last_three_chars);
    print $bytes;
  ' "$utf16_le_str"
)

参照:perlunitutPython 2 Unicode HOWTO


echoあなたの防弾ソースはありますか?
mikeserv

@mikeserv decode/encodeは私の防弾ソースです。私の答えを整理しました。
エフゲニーVereshchagin

また、バイトセットは異なる文字セットの異なる文字を反映する可能性があるため、これは正しく動作することを保証するロケール設定にも依存します。このため、「作品」LC_ALL=Cは、非常に「ダム」の設定なので、しかし、あなたはSHIFT-5にUTF-8文字列、またはKOI8などにSHIFT-5弦渡ししようとすると、それが壊れる可能性
マーティンTournoij

@Carpetsmoker、ありがとう。コメントを教えてください。私はそれperl -CAO -e 'print substr($ARGV[0], -3)'がうまくいくと思います。A@ARGV要素はUTF-8でエンコードされた文字列であることが期待され、OSTDOUTはUTF-8になります。
エフゲニーVereshchagin

への割り当てについて伝えるように見えるutf8_str
エフゲニーVereshchagin

1

「expr」または「rev」の使用はどうですか?

@ G-Manが提供するものと同様の答え:expr "$yourstring" : '.*\(...\)$' grepソリューションと同じ欠点があります。

よく知られているトリックは、「cut」と「rev」を組み合わせることです。 echo "$yourstring" | rev | cut -n 1-3 | rev


rev解決策は次のようにたくさん見えるグレン・ジャックマンさん
ジェフ・シャラー

あなたは正しいです@Jeff_Schaller:私はグレンのものを逃しました:
gildux

0

文字列のサイズを取得するには:

size=${#STRING}

次に、最後のn文字の部分文字列を取得します。

echo ${STRING:size-n:size}

例えば:

STRING=123456789
n=3
size=${#STRING}
echo ${STRING:size-n:size}

与えるだろう:

789


-1

stringにスペースが含まれている場合、printfは機能しません。

スペースを含む文字列のコードの下

str="Welcome to Linux"
echo -n $str | tail -c 3

ヌクス


うまくprintfいかない場合は、何か非常に悪いことをしています。
クサラナナンダ

1
@Kusalananda:Saurabhが示すコマンドに基づいて、彼らはprintf $strprintf "$str"またはではなくprintf '%s' "$str")試みました。そして、はい、printf $strである非常に間違っています。(echo -n $strそれほど良くはありません。)
G-Manが「Reinstate Monica」と言う
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.