Bashを使用してすべてのサブディレクトリでアクションを実行する


194

特定のフォルダのすべてのサブディレクトリでアクションを実行する必要があるスクリプトに取り組んでいます。

それを書く最も効率的な方法は何ですか?


1
戻って戻って正解を再評価することを検討してください-大きなバグ(f / e、以前に実行されたディレクトリ上で実行mkdir 'foo * bar'するとfoo、原因とbarなる場合でも繰り返される)それらは存在せず、*すべてのファイル名のリストに置き換えられます。ディレクトリ以外のものも含まれます)。
Charles Duffy、

1
...さらに悪いのは、誰かがmkdir -p '/tmp/ /etc/passwd /'実行した場合です/tmp。たとえば、誰かがこのプラクティスに従ってスクリプトを実行すると、たとえば、削除するディレクトリを見つけると、最終的に削除される可能性があり/etc/passwdます。
Charles Duffy、

回答:


177
for D in `find . -type d`
do
    //Do whatever you need with D
done

81
これは空白で壊れます
ghostdog74

2
また、find再帰的または非再帰的な動作が必要な場合は、パラメーターを微調整する必要があることに注意してください。
Chris Tonkinson、

32
上記の答えは私にもセルフディレクトリを与えたので、以下は私にとって少しうまくいきました: find . -mindepth 1 -type d
jzheaux

1
@JoshC、両方の亜種はによって作成さmkdir 'directory name with spaces'れたディレクトリを4つの別々の単語に分割します。
Charles Duffy、

4
追加する必要があった、-mindepth 1 -maxdepth 1または深すぎた。
ScrappyDev

282

サブプロセスの作成を回避するバージョン:

for D in *; do
    if [ -d "${D}" ]; then
        echo "${D}"   # your processing here
    fi
done

または、アクションが単一のコマンドの場合、これはより簡潔です。

for D in *; do [ -d "${D}" ] && my_command; done

または、さらに簡潔なバージョン(@enzotibに感謝)。このバージョンでは、の各値にD末尾にスラッシュがあることに注意してください。

for D in */; do my_command; done

48
あなたは避けることができるif[と:for D in */; do
enzotib

2
+1は、受け入れられた回答とは対照的に、ディレクトリ名が./で始まっていないため
HayriUğurKoltuk 14

3
これは、ディレクトリ名のスペース+1まででも正しい
Alex Reinking

5
最後のコマンドには1つの問題があります。サブディレクトリのないディレクトリにいる場合。「* /」を返します。だから、より良い2番目のコマンドを使用for D in *; do [ -d "${D}" ] && my_command; doneまたは2最新の組み合わせ:for D in */; do [ -d $D ] && my_command; done
クリスマース

5
この回答は隠しディレクトリを無視することに注意してください。隠しディレクトリを含めるには、for D in .* *; do代わりにを使用してくださいfor D in *; do
patryk.beza

105

最も単純な非再帰的な方法は次のとおりです。

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

/最後には、ディレクトリのみを使用し、伝えます。

の必要はありません

  • 見つける
  • awk
  • ...

11
注:これにはドットディレクトリが含まれません(これは良いことですが、知っておくことが重要です)。
wisbucky 2017

shopt -s dotglobワイルドカードを展開するときにdotfiles / dotdirsを含めることができることに注意してください。参照:gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
SteenSchütt18年

使いたいパスを表すのでは/*なく、という意味だと思います。*//
Brōtsyorfuzthrāx

2
@Shule /*は絶対パス用ですが*/、現在の場所からのサブディレクトリが含まれます
Dan G

3
役立つヒント:$ dから末尾の「/」を${d%/*}
削除

18

findコマンドを使用します。

GNU findでは、-execdirパラメーターを使用できます。

find . -type d -execdir realpath "{}" ';'

または-execパラメータを使用して:

find . -type d -exec sh -c 'cd -P "$0" && pwd -P' {} \;

またはxargsコマンドで:

find . -type d -print0 | xargs -0 -L1 sh -c 'cd "$0" && pwd && echo Do stuff'

またはforループを使用:

for d in */; { echo "$d"; }

再帰性については、**/代わりに拡張グロビング()を試してください(有効にする方法:)shopt -s extglob


その他の例については、各ディレクトリに移動してコマンドを実行する方法を参照してくださいSOで


1
-exec {} +はPOSIX仕様で-exec sh -c 'owd=$PWD; for arg; do cd -- "$arg" && pwd -P; cd -- "$owd"; done' _ {} +あり、もう1つの有効なオプションであり、起動するシェルの数は未満です-exec sh -c '...' {} \;
チャールズダフィー

13

便利なワンライナー

for D in *; do echo "$D"; done
for D in *; do find "$D" -type d; done ### Option A

find * -type d ### Option B

オプションAは、間にスペースがあるフォルダーに適しています。また、フォルダー名の各単語を個別のエンティティとして出力しないため、一般的に高速です。

# Option A
$ time for D in ./big_dir/*; do find "$D" -type d > /dev/null; done
real    0m0.327s
user    0m0.084s
sys     0m0.236s

# Option B
$ time for D in `find ./big_dir/* -type d`; do echo "$D" > /dev/null; done
real    0m0.787s
user    0m0.484s
sys     0m0.308s

10

find . -type d -print0 | xargs -0 -n 1 my_command


その検索の戻り値をforループにフィードできますか?これはより大きなスクリプトの一部です...
mikewilliamson

@Mike、ありそうもない。$?おそらく、ではなく、findまたはxargsコマンドのステータスを取得しますmy_command
ポールトンブリン

7

これにより、サブシェルが作成されます(つまり、whileループが終了すると変数の値が失われます)。

find . -type d | while read -r dir
do
    something
done

これはしません:

while read -r dir
do
    something
done < <(find . -type d)

ディレクトリ名にスペースが含まれている場合はどちらでも機能します。


奇妙なファイル名(空白で終わる名前や改行を含む名前を含む)の処理をさらに向上させるには、find ... -print0andを使用しますwhile IFS="" read -r -d $'\000' dir
Gordon Davisson

@GordonDavissonは、...確かに、私もそれが主張したい-d ''ことから、bashの構文と機能について誤解を招く以下である-d $'\000'こと(誤って)意味$'\000'から何らかの方法が異なるにある''推論から(偽って、そして再び)1が容易にできた、確かに- bashがC文字列(NUL区切り、NULを含めることができない)ではなく、Pascalスタイルの文字列(長さ指定、NULリテラルを含めることができる)をサポートしていること。
Charles Duffy、

5

あなたは試すことができます:

#!/bin/bash
### $1 == the first args to this script
### usage: script.sh /path/to/dir/

for f in `find . -maxdepth 1 -mindepth 1 -type d`; do
  cd "$f"
  <your job here>
done

または類似...

説明:

find . -maxdepth 1 -mindepth 1 -type d:唯一の1の1の最大再帰深度($ 1のサブディレクトリのみ)と最小深さ(除く現在のフォルダとディレクトリを見つけます.


これはバグがあります-スペースを含むディレクトリ名で試してください。BashPitfalls#1DontReadLinesWithForを参照してください。
Charles Duffy、

スペースを含むディレクトリ名は引用符で囲まれているため機能し、OPはファイルから行を読み取ろうとしません。
Henry Dobson、

それはで動作しますcd "$f"。からの出力findが文字列分割の場合は機能しません。そのため、名前の個別の部分がの個別の値としてあり、の展開の$f意味を引用したり、引用したりしません$f
Charles Duffy、

彼らファイルから行を読み取ろうとしているとは言わなかった。findの出力は、デフォルトの-printアクションを使用した行指向(1行に1つの名前を付けたものですが、理論的には-以下を参照)です。
Charles Duffy、

fromのような行指向の出力は、任意のファイル名を渡す安全な方法でfind -printはありません。何かを実行して、その名前に別の行として含まれるディレクトリを取得できるからです。内のファイルまたはディレクトリの名前を注意深く処理することは、権限昇格攻撃を受けるための優れた方法です。mkdir -p $'foo\n/etc/passwd\nbar'/etc/passwd/upload/tmp
チャールズダフィー

4

受け入れられた回答は、ディレクトリ名に空白がある場合は空白で中断します$()。推奨される構文は、bash / kshです。GNU find -exec オプションを使用して、+;例えば

find .... -exec mycommand +; #this is same as passing to xargs

または、whileループを使用する

find .... |  while read -r D
do
  ...
done 

ディレクトリ名を保持するパラメータは何ですか?例:chmod + x $ DIR_NAME(はい、私はディレクトリにのみchmodオプションがあることを知っています)
Mike Graf

間に1つの微妙な違いがあるfind -execとに渡すxargsfindしばらくは、実行中の命令の終了値を無視しxargs、ゼロ以外の終了に失敗しますが。ニーズによっては、どちらが正しいかもしれません。
Jason Wodicka

find ... -print0 | while IFS= read -r dより安全です-空白で始まるまたは終わる名前、および改行リテラルを含む名前をサポートします。
Charles Duffy、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.