パイピング結果を出力するときに奇妙なパス文字を処理する方法は?

パイピング結果を出力するときに奇妙なパス文字を処理する方法は?

コマンドで各パスの長さを見つけたいという問題がありますfind。私の最初の試みは、次のことを実行することでした。

find . -exec sh -c "echo {} | wc -c" \;

この答えからアイデアを得ました。。 (上記のコマンドはいいえ私の問題は単に例を挙げることです、それは完全に人工的です。また、時には複数のパイプが必要な場合があります。 )

ただし、実行すると、出力パスにある特殊文字が原因で出力にエラーが発生します。残念ながら、どのパスが問題を引き起こしているかを解決する方法がわかりませんが、エラーメッセージには情報がありません。にもかかわらず...

後でこの回答を偶然見つけました。:

コマンドはfindコマンドを直接実行します。このコマンド(filename引数を含む)は、シェルまたはファイル名を変更できる他の項目によって処理されません。とても安全です。

これはとても便利なようです。実はとても便利なので、-exec sh -c ...「治療」は病気自体よりも悪いようです。

だから私の質問は、私はいつ必要パイプコマンドfindと私のパスに特殊文字を含めることはできますか?この問題に対する普遍的な解決策はありますか?いくつかの考慮事項を考える必要はありませんか?バッシュを使っています。


注:これは同様の質問です。find + execコマンドの出力をパイプに送信する最良の方法は何ですか?違いは出力を必ず-execfind ... -exec ... foo {} | bar \;私は抵抗が最も少ない共通パスを探しているだけで、コマンドの構造は私にとって重要ではありません。

答え1

ファイル名をシェルスクリプトに引数として渡します。

find . -exec sh -c 'printf "%s\n" "$1" | wc -c' sh {} \;

または、シェルごとに複数のファイルを呼び出します。

find . -exec sh -c 'for x in "$@"; do printf "%s\n" "$x" | wc -c; done' sh {} +

注文

find . -exec sh -c "echo {} | wc -c" \;

ファイル名はシェルコマンドラインにそのまま挿入されます。スペースまたはシェル固有の文字を含まないファイル名にのみ機能します。たとえば、Don't stop me now.mp3同じことはthis&that.txt問題を引き起こす可能性があります。 (最初は終わらない引用文字列を生成し、2番目はechoバックグラウンドで始まり、名前付きコマンドを実行しようとしますthat.txt。)

一方、sh -c ... sh {} \;(またはファイル名が別の引数としてシェルに渡された... {} +場合findは、位置引数で使用でき、シェル構文と混合せずに使用できます。(最初の"$1"場合は"$@"全体について)リスト。)

ファイル名の長さを確認する場合は、"${#var}"次のように長さを指定することを除き、シェルからインポートすることもできます。数値現在のロケールに基づいて同時にwc -c計算バイト

答え2

-exec echo {}避けているにもかかわらずシェル処理、複数バージョンエコmangle パラメーターにはバックスラッシュまたは先行ハイフンが含まれます。 (もちろん、wc必要に応じて送信されません。)

すべてのパス名に対してこれを行うのではなく、複数のwc入力行(レコードとも呼ばれる)を処理するように設計されたプログラムを使用します。

find . | awk '{print length}'   # basecase

# if in a multibyte locale and you want bytes not chars
# prefix the awk with LANG=C (or any other singlebyte)

# if pathname (ever) contains newline, and you have GNU find (and awk?)
find . -print0 | awk -vRS='\0' '{print length}' 

または、デフォルトはバイトですが、改行が許可されている場合をperl -nle 'print length'処理する方法が見つかりませんでした。-print0

関連情報