bashでnullで区切られた入力で「頭」と「尾」を実行する方法は?

bashでnullで区切られた入力で「頭」と「尾」を実行する方法は?

findこのコマンドは、ファイル名をnullで区切られた文字列(提供されている-print0場合)として出力することができ、オプションがオンの状態で使用できますxargs-0しかし、その間には、対応するファイルのコレクションを操作することは困難です。sortコマンドには-zファイルを並べ替えるスイッチがありますが、head含まれていませんtail

これらのnullで区切られた入力を便利な方法でどのようにheadANDできますか?tail(いつでも短くて遅いRubyスクリプトを作成できますが、より良い方法があることを願っています)

答え1

GNUheadおよびtailcoreutilsバージョン8.25以降の-zオプションがあります。

以前のバージョンまたはGNU以外のシステムでは、次のように\0交換してみることができます\n

find ... -print0 |
  tr '\0\n' '\n\0' |
  head |
  tr '\0\n' '\n\0'

一部のhead実装ではNUL文字を処理できませんが(POSIXではこれを必要としません)、findは処理し、テキスト-print0ユーティリティheadは通常NUL文字をサポートします。

tr関数を使用して2つの間のコマンドをラップすることもできます。

nul_terminated() {
  tr '\0\n' '\n\0' | "$@" | tr '\0\n' '\n\0'
}

find ... -print0 | nul_terminated tail -n 12 | xargs -r0 ...

下のnul_terminatedaは\0改行文字を表します。たとえば、\n次のように変更します_

find . -depth -name $'*\n*' -print0 | nul_terminated sed '
  p;h;s,.*/,,;s/\x0/_/g;H;g;s,[^/]*\n,,' | xargs -r0n2 mv

\x0また、GNU拡張)。

複数を実行する必要がある場合フィルターコマンドを使用すると、次のことができます。

find ... -print0 |
  nul_terminated cmd1 |
  nul_terminated cmd2 | xargs -r0 ...

ただし、これはいくつかの冗長trコマンドを実行することを意味します。または、以下を実行できます。

find ... -print0 | nul_terminated eval 'cmd1 | cmd2' | xargs -r0 ...

答え2

以前のシステムでは、nullで区切られた入力がサポートされていたため、grep3つの最新のファイルをインポートする代わりにこれを実行しました。head --zero-terminated -n 3

find . -maxdepth 1 -type f -printf "%T@ %p\0" | sort -zrn | grep -zm 3 ""

sortとの組み合わせを使用すると、grep置き換えによってtail3番目の最新のファイルを取得できます。

find . -maxdepth 1 -type f -printf "%T@ %p\0" | sort -zrn | grep -zm 3 "" | sort -zn | grep -zm 1 ""

ファイル名のみ:

find . -maxdepth 1 -type f -printf "%T@ %p\0" | sort -zrn | grep -zm 3 "" | sort -zn | grep -zPom 1 "(?s)/.*"

残念ながら、-o(一致のみ)ゼロで区切られた出力(バグ!?)を無効にします。これは次のようにアクティブになり、末尾の改行を含む-zファイル名を返すため、追加のパイピングは許可されません。

find . -maxdepth 1 -type f -printf "%T@ %p\0" | sort -zrn | grep -zm 3 "" | sort -zn | grep -zPom 1 "(?s)/.*" | xargs -0 ls -l
ls: cannot access /te
st
: No such file or directory

この問題は、以下を使用して解決できますread

find . -mindepth 1 -maxdepth 1 -type f -printf "%T@ %p\0" | sort -zrn | grep -zm 3 "" | sort -zn | { IFS=" " read -rd '' mtime file && echo -en "$file\0"; } | xargs -0 -n1 ls -l
-rw-r--r-- 1 root root 0 Apr 24 17:46 ./te?st

ls -t予想通り、ファイル名の改行文字の疑問符を返します。)

または、現在のディレクトリから最も古い100ファイルをインポートする方法の別の例は次のとおりです。

find /tmp/test -mindepth 1 -maxdepth 1 -type f -printf "%T@ %p\0" | sort -zn | grep -zm 10 "" | { while IFS=" " read -rd '' mtime file; do echo -en "$file\0"; done; } | xargs -0 -n1 ls -bl
-rw-r--r-- 1 root root 0 Mar 23 14:32 /tmp/test/foo\ baz.txt
-rw-r--r-- 1 root root 0 Apr 26 10:43 /tmp/test/foo\nbar\ baz.txt
...

関連情報