find の出力をキャプチャしています。-print0をbash配列に入れる
質問
使用する find . -print0
ファイル名にはスペース、改行、引用符などが含まれる可能性があるため、bash でファイルのリストを取得する唯一の安全な方法のようです。
ただし、find の出力を bash 内や他のコマンド ライン ユーティリティで活用するのに実際には苦労しています。出力を利用する唯一の方法は、出力を perl にパイプし、perl の IFS を null に変更することです。
find . -print0 | perl -e '$/="\0"; @files=<>; print $#files;'
この例では、見つかったファイルの数を出力し、次の場合に発生するような、ファイル名の改行によってカウントが破壊される危険を回避します。
find . | wc -l
ほとんどのコマンド ライン プログラムは null 区切りの入力をサポートしていないため、最善の方法は次の出力をキャプチャすることだと思います。 find . -print0
上記の Perl スニペットで行ったように、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を実行し、その配列は、その構造を過ぎて住んでいます。警告:リダイレクトのこのフォームはSH-エミュレーションモードでbashのではなく、さらにbashでのみ利用可能です。あなたが#!/bin/bash
でスクリプトを起動する必要があります。
また、ファイル処理ステップは、(この場合には、ちょうどa[i++]="$file"
ていますが、ループの中で手の込んだ直接何かをしたい場合があります)、その入力がリダイレクトされたため、それが標準入力から読み込み可能性のあるすべてのコマンドを使用することはできません。この制限を回避するために、私が使用する傾向がある。
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は1ファイル引数とxargsの幹部do_something_usefulを行っている、あまりにもあなたのために有用である可能性があります。
主な問題であり、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
mapfile
は-d
スイッチを有し、(-d
文のread
スイッチに似た区切り文字を指定する)、および区切り文字はNULLバイトであってもよいです。したがって、タイトルに質問に答え素敵な
のbash配列に
find . -print0
の出力をキャプチャする
です。
mapfile -d '' ary < <(find . -print0)
あなたが安全にこれで、カウントを行うことができます:
find . -exec echo ';' | wc -l
は、(それが見つかりました。すべてのファイル/ディレクトリのために改行を出力して、プリントアウト改行を数える...)
もっと洗練された解決策が存在すると思いますが、私はこれを放り込みます。これは、スペースや改行を含むファイル名にも機能します。
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
このページ 良い例を示しています。詳細については、を参照してください。 第26章 の中に 高度な Bash スクリプト ガイド.
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'
私は新しいですが、私は、この答えは信じています。それは誰かに役立ちます願っています:
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
このは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[@]}
古い質問を、私は、私は思ったので、誰が、この単純な方法を提案していません。あなたのファイル名がETXを持っている場合は確かに、これはあなたの問題を解決していませんが、私はそれが任意の実際のシナリオに役立つと思います。ヌルを使用しようとすると、ルールを取り扱うデフォルトのIFSの抵触実行しているようです。検索オプションおよびエラー処理とあなたの好みに季節ます。
savedFS="$IFS"
IFS=$'\x3'
filenames=(`find wherever -printf %p$'\x3'`)
IFS="$savedFS"
ゴードン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/** )
バッシュは、ファイル名(または実際に任意のテキスト)を扱うのが得意ではありませんでした。
私の代わりに SH のライブラリでのpythonを使用してお勧めします。