find . -print0
を使用することが、スペース、改行、引用符などを含むファイル名の可能性があるため、bashでファイルのリストを取得する唯一の安全な方法のようです。
ただし、bash内または他のコマンドラインユーティリティを使用して、findの出力を実際に有効にするのに苦労しています。出力を利用することができた唯一の方法は、それをPerlにパイプし、PerlのIFSをnullに変更することです。
find . -print0 | Perl -e '$/="\0"; @files=<>; print $#files;'
この例では、見つかったファイルの数を出力します。ファイル名の改行がカウントを破壊する危険を回避します。
find . | wc -l
ほとんどのコマンドラインプログラムはnull区切りの入力をサポートしていないため、上記のPerlスニペットで行ったように、bash配列でfind . -print0
の出力をキャプチャするのが最良の方法だと思います。どんなタスクでも。
これどうやってするの?
これは機能しません:
find . -print0 | ( IFS=$'\0' ; array=( $( cat ) ) ; echo ${#array[@]} )
より一般的な質問は次のとおりです:bashのファイルのリストを使用して便利なことを行うにはどうすればよいですか?
Greg's 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)
_
...これは、stdinではなくユニット3を介してファイルリストを渡します。
多分あなたはxargsを探しています:
find . -print0 | xargs -r0 do_something_useful
オプション-L 1は、あなたにとっても有用かもしれません。これは、xargsを1つのファイル引数のみでdo_something_useful execします。
主な問題は、IFSにNUL値を割り当てることができないため、デリミタNUL(\ 0)がここでは役に立たないことです。優れたプログラマーとして、プログラムへの入力は処理可能なものであることに注意してください。
最初に、この部分を実行する小さなプログラムを作成します。
#!/bin/bash
printf "%s" "$@" | base64
...そしてbase64strと呼びます(chmod + xを忘れないでください)
次に、シンプルで簡単なforループを使用できます。
for i in `find -type f -exec base64str '{}' \;`
do
file="`echo -n "$i" | base64 -d`"
# do something with file
done
だから、トリックは、base64文字列にbashのトラブルを引き起こす兆候がないことです-もちろんxxdまたは同様のものでも仕事をすることができます。
ファイルをカウントするさらに別の方法:
find /DIR -type f -print0 | tr -dc '\0' | wc -c
Bash 4.4以降、組み込みのmapfile
には-d
スイッチがあり(read
ステートメントの-d
スイッチと同様に区切り文字を指定するため)、区切り文字はnullバイトである。したがって、タイトルの質問に対するいい答え
find . -print0
の出力をbash配列にキャプチャする
は:
mapfile -d '' ary < <(find . -print0)
これでカウントを安全に行うことができます:
find . -exec echo ';' | wc -l
(見つかったファイル/ディレクトリごとに改行を出力し、出力された改行をカウントします...)
次のことが可能な場合は、xargsを避けてください。
man Ruby | less -p 777
IFS=$'\777'
#array=( $(find ~ -maxdepth 1 -type f -exec printf "%s\777" '{}' \; 2>/dev/null) )
array=( $(find ~ -maxdepth 1 -type f -exec printf "%s\777" '{}' + 2>/dev/null) )
echo ${#array[@]}
printf "%s\n" "${array[@]}" | nl
echo "${array[0]}"
IFS=$' \t\n'
よりエレガントなソリューションが存在すると思いますが、私はこれを投げます。これは、スペースや改行を含むファイル名でも機能します:
i=0;
for f in *; do
array[$i]="$f"
((i++))
done
その後、例えばファイルを1つずつリストします(この場合は逆順)。
for ((i = $i - 1; i >= 0; i--)); do
ls -al "${array[$i]}"
done
このページ はニースの例を示しています。詳細は Advanced Bash-Scripting Guide の Chapter 26 をご覧ください。
私は新しいですが、これは答えだと思います。それが誰かを助けることを願っています:
STYLE="$HOME/.fluxbox/styles/"
declare -a array1
LISTING=`find $HOME/.fluxbox/styles/ -print0 -maxdepth 1 -type f`
echo $LISTING
array1=( `echo $LISTING`)
TAR_SOURCE=`echo ${array1[@]}`
#tar czvf ~/FluxieStyles.tgz $TAR_SOURCE
古い質問ですが、誰もこの簡単な方法を提案しなかったので、私はそうするだろうと思いました。ファイル名にETXが含まれている場合は問題ありませんが、これで問題は解決しませんが、実際のシナリオには役立つと思われます。 nullを使用しようとすると、デフォルトのIFS処理ルールに違反するようです。検索オプションとエラー処理で好みに合わせて調整します。
savedFS="$IFS"
IFS=$'\x3'
filenames=(`find wherever -printf %p$'\x3'`)
IFS="$savedFS"
これはStephan202のバージョンに似ていますが、ファイル(およびディレクトリ)は一度に配列に入れられます。ここでのfor
ループは、単に「役に立つことをする」ためのものです。
files=(*) # put files in current directory into an array
i=0
for file in "${files[@]}"
do
echo "File ${i}: ${file}" # do something useful
let i++
done
カウントを取得するには:
echo ${#files[@]}
Gordon Davissonの答えはbashに最適です。ただし、zshユーザーには便利なショートカットがあります。
まず、変数に文字列を配置します。
A="$(find /tmp -type f -print0)"
次に、この変数を分割して配列に保存します。
B=( ${(s/^@/)A} )
トリックがあります:^@
はNUL文字です。そのためには、Ctrl + Vに続けてCtrl + @を入力する必要があります。
$ Bの各エントリに正しい値が含まれていることを確認できます。
for i in "$B[@]"; echo \"$i\"
注意深い読者であれば、ほとんどの場合、find
コマンドの呼び出しは**
構文。例えば:
B=( /tmp/** )