名前に\ n文字を含むフォルダーのforループ


9

私はいくつかのフォルダに\n名前と文字を付けています。

例えば:

$ ls
''$'\n''Test'

これは、テスト名があり、名前の前に空行があるフォルダーを参照しています。

したがって、このようなスクリプトをその親ディレクトリで実行すると、次のようになります。

while IFS= read -r d; do 
    rmdir $d
done < <(find * -type d)

それが示している:

rmdir: failed to remove '': No such file or directory
rmdir: failed to remove 'Test': No such file or directory

フォルダー名に2行あるため、2回実行さ\nTestます。

では、この問題を解決するにはどうすればいい\nTestですか?


3
findの-print0ディレクティブと-dreadオプションを使用する必要があります。stackoverflow.com/a/40189667/7552を
glenn jackman

1
@glennjackman答えてください!
デザート

@glennjackman返信ありがとうございます。find * -type d -print0 | while IFS= read -d '' file ; do rmdir $file ; doneコマンドにはこの出力があります rmdir: failed to remove 'Test': No such file or directory
Tara S Volpe

5
あなたがしなければならない変数を引用:rmdir "$file"
グレンはジャックマン

1
@glennjackmanこれを回答として投稿してください。それは適切な解決策であり、コメント以外はそのための最良の場所ではありません。暗黙の賛成投票:)
Sergiy Kolodyazhnyy

回答:


12

そこにはコマンドが1つしかないのでfind-execflag呼び出しで呼び出すだけで十分rmdirです。

find -depth -type d -exec rmdir {} \;

または、のように-deleteオプションを使用しますが find -type d -delete、空でないディレクトリでは機能しません。そのためには-emptyフラグも必要になります。また、スキップされる可能性があることを-delete意味-depthします。したがって、すべてを1つのプロセスとして保持する、もう1つの実行可能な代替手段です。

find -type d -empty -delete

ディレクトリが空でない場合は、を使用しますrm -rf {} \;\nファイル名がinのディレクトリのみを分離するには、bashのANSI-Cの引用 $'...'-nameオプションを組み合わせます。

find  -type d -name $'*\n*' -empty -delete

POSIX-ly、私たちはそれをこのように扱うことができます:

find -depth  -type d -name "$(printf '*\n*' )" -exec rmdir {} \;

言及する価値があるのは、目的がディレクトリの削除である場合はそれ-deleteで十分ですが、ディレクトリでコマンドを実行する場合-execは最も適切です。

こちらもご覧ください


2
rm -rf 注意して使用してください。; Pはないfind持っている-deleteところでオプションを?空のディレクトリを削除し、空ではないディレクトリに対してエラーをスローしrmdirます。
デザート

3
@dessert実行して追加しましたが、-delete空でないディレクトリは削除されません。したがって、慎重な使用rm -rf
セルギー・コロディアズニー

6
影響を受けるファイルのリストが実際に正しく、削除してはならないものが含まれていないかどうかを確認findするために、破壊的なペイロードなしで(-deleteまたはを削除して-exec whatever \;)そのようなコマンドを常にドライランしてください...
バイトコマンダー

2
これはaskubuntu.comなので、GNUを想定できますfind-exec rmdir {} +複数の引数を1つのrmdirコマンドラインにバッチ処理するために使用します。ところで、「しかし、それは空でないディレクトリでは動作しません。」も同様に適用さrmdirれるので、実際にはとの違いはありません-delete。との違いですrm -r
Peter Cordes

2
find -type d -empty -delete(を-delete意味します-depth)。
Kevin

10

あなたは代わりにシェルグロブを使うことができますfind

for d in */ ; do 
    rmdir "$d"
done

シェルグロブ*/は、現在のディレクトリ内のすべてのフォルダに一致します。このforループ構造は、正しい単語分割を自動的に処理します。

シェルオプションによっては、隠しフォルダー(。で始まる名前)が無視される場合があることに注意してください。コマンドを使用して、現在のセッションのすべてのファイルに一致するようにその動作を変更できますshopt -s dotglob

また、常に変数を引用することを忘れないでください。


グロブだけではない再帰的に同じように、現在のディレクトリ内のファイル/ディレクトリを見つけるfind
ilkkachu

1
あなたは正しい@ilkkachu。これは、globstarシェルオプションshopt -s globstarを有効にし、**/*/代わりにglob を使用することで変更できます。
バイトコマンダー

6

これまでに書かれた両方の回答はrmdir、ディレクトリごとに1回呼び出されrmdirますが、複数の引数を取る可能性があるので、もっと効率的な方法はありませんか?

簡単にできる

rmdir */

これは間違いなく最も簡単で効率的な方法ですが、多くのディレクトリの場合はエラーがスローされる可能性があります(gnome-terminalのコマンドライン引数の最大長は?を参照)。このアプローチを再帰的に機能させたい場合は、globstarシェルオプションを有効にして、の代わりにshopt -s globstar使用してください。**/*/*/

GNU findを使用すると(そして単にを使用したくない場合-delete)、

find -depth -type d -exec rmdir {} +

「コマンドラインをxargs構築するのとほとんど同じ方法で」コマンドラインを構築します(man find)。再帰的に動作させたくない場合は、代わりに使用-depth-maxdepth 1てください。

3番目のIMOの優れた方法は、この回答で steeldriverによって説明されています

printf '%s\0' */ | xargs -0 rmdir

これは、組み込みのシェルを使用しprintfてゼロ区切りの引数リストを作成します。このリストはパイプで渡され、必要なだけ正確xargsに呼び出さrmdirれます。上記のようにshopt -s globstar**/*/代わりに再帰的に動作させることができ*/ます。


2
find再帰的ですが、OPのループはそうではありません。その動作を再現するには、を使用しますfind -maxdepth 1 -type d -exec rmdir {} +。(-depthその場合、冗長です。)printfエミュレートするためのきちんとしたトリックfind -print0、私は以前にそれを見たことはありませんでした。
Peter Cordes

1
ああ!とループを確認しましたがlswhileループfindにパイプlsするのではなく、プロセス置換からリダイレクトしていて、なんらかの理由で表示されていないことに気が付きませんでした。それfind *は本当に埋もれています。はい、これは再帰的であり、ディレクトリを使用しないため、ディレクトリ以外のディレクトリを含むディレクトリエントリが多すぎる場合は失敗します*/。 bashのglob拡張よりもingの方が速いfind .ため、はるかに優れています。findstat
Peter Cordes
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.