名前にスペースが含まれているファイルをループしますか? [重複]

NULバイトをサポートしていない場合は、空の文字列として扱われます。

正確には、-rも追加します。これは、でバックスラッシュを処理しないことを示しています。特別にファイル名。たとえば、-rがない場合、\<newline>は削除され、\n

bashやまたはnullバイトに関する上記のすべてのルールを覚えておく(これもGillesのおかげです):

find . -name "*.csv" -exec sh -c " file="$0" echo "$file" diff "$file" "/some/other/path/$file" read char </dev/tty " exec-sh {} ";" 

* 3。名前の末尾が .csv

find . -name "*.csv" 

は、something.csv

これを回避するには、-type ffindコマンドに追加します。

find . -type f -name "*.csv" -exec sh -c " file="$0" echo "$file" diff "$file" "/some/other/path/$file" read line </dev/tty " exec-sh {} ";" 

glenn jackman が指摘しているように、これらの例の両方で、各ファイルに対して実行するコマンドは次のとおりです。サブシェルで実行されるため、ループ内で変数を変更すると、それらは忘れられます。

変数を設定する必要があり、それを設定したままにする場合ループの最後で、次のようなプロセス置換を使用するように書き直すことができます。

i=0 while IFS= read -r -d "" file; do echo "file = $file" diff "$file" "/some/other/path/$file" read line </dev/tty i=$((i+1)) done < <(find . -type f -name "*.csv" -print0) echo "$i files processed" 

コマンドラインでこれをコピーして貼り付ける場合は、注意してください。 、read lineecho "$i files processed"を消費するため、コマンドは実行されません。

これを回避するには、 read line </dev/ttyを削除し、結果をlessのようなポケットベルに送信できます。


内部のセミコロン(;)を削除しましたループ。必要に応じて元に戻すこともできますが、必須ではありません。

最近では、$(command)`command`。これは主に、`command1 \`command2\``よりも$(command1 $(command2))の方が簡単に記述できるためです。

read charは実際には文字を読みません。行全体を読み取るので、read lineに変更しました。

コメント

  • puting

    は、作成されたサブシェルで問題を引き起こす可能性があります(たとえば、コマンドの完了後にループブロック内の変数が表示されなくなります)。 bashでは、入力リダイレクトとプロセス置換を使用します:while read -r -d $'\0' file; do ...; done < <(find ... -print0)

  • もちろん、またはヒアドキュメントを使用します:while read; do; done <<EOF "$(find)" EOF 。しかし、それほど読みやすいものではありません。
  • @glenn jackman:今、さらに説明を追加しようとしました。良くしたのか悪くしたのか?
  • ' IFS, -print0, whilefindを完全に処理すると、以下の私のソリューションに示されます。
  • 最初のソリューションは、改行以外のすべての文字に対応します。 set -fでグロブもオフにする場合。

回答

ファイル名にスペースまたはシェルグロブ文字が含まれている場合、このスクリプトは失敗します\[?*findコマンドは、1行に1つのファイル名を出力します。次に、コマンド置換`find …`がシェルによって次のように評価されます。

  1. findコマンドを実行します。出力を取得します。
  2. findの出力を別々の単語に分割します。空白文字はすべて単語区切り文字です。
  3. 各単語について、グロブパターンの場合は、一致するファイルのリストに展開します。

たとえば、現在のディレクトリに、`foo* bar.csvfoo 1.txtfoo 2.txtという3つのファイルがあるとします。

  1. findコマンドは./foo* bar.csvを返します。
  2. シェルはこの文字列を分割しますスペースで、./foo*bar.csvの2つの単語を生成します。
  3. ./foo*にはグロブメタ文字が含まれており、一致するファイルのリストに展開されます:./foo 1.txtおよび./foo 2.txt
  4. したがって、forループは、./foo 1.txt./foo 2.txt、および

この段階では、単語の分割を減らして方向転換することで、ほとんどの問題を回避できます。グロビングをオフにします。単語の分割を減らすには、IFS変数を単一の改行文字に設定します。このようにして、findの出力は改行でのみ分割され、スペースは残ります。グロビングをオフにするには、set -fを実行します。次に、ファイル名に改行文字が含まれていない限り、コードのこの部分は機能します。

IFS=" " set -f for file in $(find . -name "*.csv"); do … 

(これは問題の一部ではありませんが、私は`…`ではなく$(…)を使用することをお勧めします。同じ意味ですが、逆引用バージョンには奇妙な引用規則があります。)

以下に別の問題があります:diff $file /some/other/path/$file

diff "$file" "/some/other/path/$file" 

そうでない場合、$fileは単語に分割され、上記のコマンドsubstitutioのように、単語はグロブパターンとして扱われます。シェルプログラミングについて1つ覚えておく必要がある場合は、次のことを覚えておいてください。変数展開($foo)とコマンド置換($foo)は常に二重引用符で囲みます。 $(bar)(分割することがわかっている場合を除く)。 (上記では、findの出力を行に分割したいと考えていました。)

findは、見つかったファイルごとにコマンドを実行するように指示しています。

find . -name "*.csv" -exec sh -c " echo "$0" diff "$0" "/some/other/path/$0" " {} ";" 

この場合、別のアプローチは2つのディレクトリを比較することですが、すべての「退屈な」ファイルを明示的に除外します。

diff -r -x "*.txt" -x "*.ods" -x "*.pdf" … . /some/other/path 

コメント

  • I ' dは、適切に引用するもう1つの理由としてワイルドカードを忘れていました。ありがとう! 🙂
  • find -exec sh -c 'cmd 1; cmd 2' ";"の代わりに、find -exec cmd 1 {} ";" -exec cmd 2 {} ";"を使用する必要があります。これは、シェルがパラメーターをマスクする必要があるためです。しかし、' tは見つかりません。ここでの特別なケースでは、echo " $ 0 "は'である必要はありません。スクリプトの一部として、';'の後に-printを追加するだけです。 '続行するための質問は含まれていませんでしたが、以下に示すように、findで行うこともできます。 ;)
  • @userunknown:find -execのパラメーターの部分文字列としての{}の使用は移植できません、その'がシェルが必要な理由です。'「シェルがパラメータをマスクする必要がある」とはどういう意味かわかりません。引用について'の場合、私のソリューションは適切に引用されています。 ' echoの部分は-printで実行できます。 -okdirはごく最近のGNU検索拡張機能であり、'はどこでも利用できるわけではありません。 '続行するまでの待機時間を含めませんでした。これは、UIが非常に貧弱で、質問者がシェルスニペットにreadを簡単に配置できると考えているためです。彼は望んでいます。
  • 引用はマスキングの一形態です、'ではありませんか? 'ポータブルなものとそうでないものについてのあなたの意見を理解していません。あなたの例(下から2番目)は-execを使用してshを呼び出し、{}を使用しています-したがって、私の例はどこにありますか(-okdirの横)ポータブル? find . -name "*.csv" -exec diff {} /some/other/path/{} ";" -print
  • 「マスキング」はシェルの文献では一般的な用語ではないため' “>

理解したい場合は、意味を説明する必要があります。私の例では、{}を1回だけ別の引数で使用しています。その他の場合(2回または部分文字列として使用)は移植できません。 「ポータブル」とは、'すべてのUNIXシステムで機能することを意味します。適切なガイドラインは、 POSIX / SingleUnix仕様です。

回答

readarrayが言及されていないことに驚いています。<<<演算子:

$ touch oneword "two words" $ readarray -t files <<<"$(ls)" $ for file in "${files[@]}"; do echo "|$file|"; done |oneword| |two words| 

<<<"$expansion"構文を使用すると、新しい行を含む変数を次のように配列に分割することもできます。 :

$ string=$(dmesg) $ readarray -t lines <<<"$string" $ echo "${lines[0]}" [ 0.000000] Initializing cgroup subsys cpuset 

readarrayはBashに何年も存在しているので、これはおそらく標準的な方法です。これはBashで。

回答

Afaikfindには必要なものがすべて揃っています。

find . -okdir diff {} /some/other/path/{} ";" 

findは、プログラムを節約的に呼び出すために自動的に処理します。-okdirは、diffの前にプロンプトを表示します(はい/いいえでよろしいですか)。

シェルは含まれず、グロビング、ジョーカー、 pi、pa、po。

補足として:findをfor / while / do / xargsと組み合わせると、ほとんどの場合、y間違っている。 🙂

コメント

回答

任意のファイル(任意の特殊文字を含む)をループします。 完全に安全な検索(ドキュメントについてはリンクを参照):

exec 9< <( find "$absolute_dir_path" -type f -print0 ) while IFS= read -r -d "" -u 9 do file_path="$(readlink -fn -- "$REPLY"; echo x)" file_path="${file_path%x}" echo "START${file_path}END" done 

コメント

回答

ここで明らかなzshソリューションについて誰も言及していないことに驚いています:

for file (**/*.csv(ND.)) { do-something-with $file } 

(D)隠しファイルも含める場合、(N)一致しない場合にエラーを回避する場合、(.)通常のファイルに制限します。)

bash4.3以降では、部分的にもサポートされるようになりました:

shopt -s globstar nullglob dotglob for file in **/*.csv; do [ -f "$file" ] || continue [ -L "$file" ] && continue do-something-with "$file" done 

回答

スペースを含むファイル名は、コマンドラインで複数の名前のように見えます。 “ファイルの名前が「HelloWorld.txt」の場合、差分行は次のように展開されます。

diff Hello World.txt /some/other/path/Hello World.txt 

これは4つのファイル名のように見えます。引数の前後の引用:

diff "$file" "/some/other/path/$file" 

コメント

回答

二重引用符はあなたの友達です。

diff "$file" "/some/other/path/$file" 

それ以外の場合、変数の内容は単語分割されます。

コメント

file./Helloに設定され、次にWorld.csv$fileを引用すると'役に立ちません。

回答

bash4では、組み込みのmapfile関数を使用して、各行を含む配列を設定し、この配列を反復処理することもできます。

$ tree . ├── a │ ├── a 1 │ └── a 2 ├── b │ ├── b 1 │ └── b 2 └── c ├── c 1 └── c 2 3 directories, 6 files $ mapfile -t files < <(find -type f) $ for file in "${files[@]}"; do > echo "file: $file" > done file: ./a/a 2 file: ./a/a 1 file: ./b/b 2 file: ./b/b 1 file: ./c/c 2 file: ./c/c 1 

回答

値のスペースは、単純なforループ構造で回避できます

for CHECK_STR in `ls -l /root/somedir` do echo "CHECKSTR $CHECK_STR" done 

ls -l root / somedir cスペースを含むファイルを保持します

スペースを含むファイルの上に出力

この出力を回避するための簡単な解決策(二重引用符に注意)

for CHECK_STR in "`ls -l /root/somedir`" do echo "CHECKSTR $CHECK_STR" done 

スペースを入れてファイルを出力する

bashで試してみました

コメント

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です