1. ホーム
  2. arrays

findの出力をキャプチャする . -print0 の出力を bash の配列に取り込む

2023-08-24 21:52:19

質問

使用方法 find . -print0 は、ファイル名にスペースや改行、引用符などが含まれる可能性があるため、bashでファイルのリストを取得する唯一の安全な方法であるように思われます。

しかし、findの出力をbash内や他のコマンドラインユーティリティで実際に役立たせるのに苦労しています。私が出力を利用することができた唯一の方法は、それを perl にパイプし、perl の IFS を null に変更することです。

find . -print0 | perl -e '$/="\0"; @files=<>; print $#files;'

この例では、見つかったファイルの数を表示し、ファイル名の改行がカウントを破損する危険を回避しています。

find . | wc -l

ほとんどのコマンドライン プログラムは null 区切りの入力をサポートしていないので、一番良いのは、以下の出力をキャプチャすることだと思います。 find . -print0 の出力をbashの配列に取り込み、それが何であれ、タスクを続行することだと思います。

どうすればいいのでしょうか?

これはうまくいきません。

find . -print0 | ( IFS=$'\0' ; array=( $( cat ) ) ; echo ${#array[@]} )

もっと一般的な質問かもしれません。 bash でファイルのリストを使って便利なことをするにはどうしたらいいですか?

どのように解決するのですか?

以下から盗用 グレッグの BashFAQ :

unset a i
while IFS= read -r -d $'\0' file; do
    a[i++]="$file"        # or however you want to process each file
done < <(find /tmp -type f -print0)

ここで使われているリダイレクト構文( cmd1 < <(cmd2) ) は、より一般的なパイプライン ( cmd2 | cmd1 ) -- コマンドがシェル組み込みの場合 (例. while など) の場合、パイプラインバージョンではサブシェルで実行され、それらが設定する変数 (例えば配列の a など) は終了時に失われます。 cmd1 < <(cmd2) はサブシェルで cmd2 を実行するだけなので、配列はその構築後も存続します。 警告: このリダイレクトは bash でのみ有効であり、sh-emulation モードの bash でさえ有効ではありません。 #!/bin/bash .

また、ファイル処理ステップ(この場合、単に a[i++]="$file" ループの中で直接もっと派手なことをしたいかもしれません) の入力がリダイレクトされるので、stdin から読み込むようなコマンドは一切使えません。 この制限を避けるために、私はしばしば

unset a i
while IFS= read -r -u3 -d $'\0' file; do
    a[i++]="$file"        # or however you want to process each file
done 3< <(find /tmp -type f -print0)

...これは標準入力ではなく、ユニット3経由でファイルリストを渡します。