特定のフォルダのすべてのサブディレクトリでアクションを実行する必要があるスクリプトに取り組んでいます。
それを書く最も効率的な方法は何ですか?
mkdir -p '/tmp/ /etc/passwd /'
実行した場合です/tmp
。たとえば、誰かがこのプラクティスに従ってスクリプトを実行すると、たとえば、削除するディレクトリを見つけると、最終的に削除される可能性があり/etc/passwd
ます。
特定のフォルダのすべてのサブディレクトリでアクションを実行する必要があるスクリプトに取り組んでいます。
それを書く最も効率的な方法は何ですか?
mkdir -p '/tmp/ /etc/passwd /'
実行した場合です/tmp
。たとえば、誰かがこのプラクティスに従ってスクリプトを実行すると、たとえば、削除するディレクトリを見つけると、最終的に削除される可能性があり/etc/passwd
ます。
回答:
for D in `find . -type d`
do
//Do whatever you need with D
done
find
再帰的または非再帰的な動作が必要な場合は、パラメーターを微調整する必要があることに注意してください。
find . -mindepth 1 -type d
mkdir 'directory name with spaces'
れたディレクトリを4つの別々の単語に分割します。
-mindepth 1 -maxdepth 1
または深すぎた。
サブプロセスの作成を回避するバージョン:
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
if
か[
と:for D in */; do
for D in *; do [ -d "${D}" ] && my_command; done
または2最新の組み合わせ:for D in */; do [ -d $D ] && my_command; done
for D in .* *; do
代わりにを使用してくださいfor D in *; do
。
最も単純な非再帰的な方法は次のとおりです。
for d in */; do
echo "$d"
done
/
最後には、ディレクトリのみを使用し、伝えます。
の必要はありません
shopt -s dotglob
ワイルドカードを展開するときにdotfiles / dotdirsを含めることができることに注意してください。参照:gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
/*
なく、という意味だと思います。*/
/
/*
は絶対パス用ですが*/
、現在の場所からのサブディレクトリが含まれます
${d%/*}
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で
-exec {} +
はPOSIX仕様で-exec sh -c 'owd=$PWD; for arg; do cd -- "$arg" && pwd -P; cd -- "$owd"; done' _ {} +
あり、もう1つの有効なオプションであり、起動するシェルの数は未満です-exec sh -c '...' {} \;
。
便利なワンライナー
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
find . -type d -print0 | xargs -0 -n 1 my_command
my_command
。
これにより、サブシェルが作成されます(つまり、while
ループが終了すると変数の値が失われます)。
find . -type d | while read -r dir
do
something
done
これはしません:
while read -r dir
do
something
done < <(find . -type d)
ディレクトリ名にスペースが含まれている場合はどちらでも機能します。
find ... -print0
andを使用しますwhile IFS="" read -r -d $'\000' dir
-d ''
ことから、bashの構文と機能について誤解を招く以下である-d $'\000'
こと(誤って)意味$'\000'
から何らかの方法が異なるにある''
推論から(偽って、そして再び)1が容易にできた、確かに- bashがC文字列(NUL区切り、NULを含めることができない)ではなく、Pascalスタイルの文字列(長さ指定、NULリテラルを含めることができる)をサポートしていること。
あなたは試すことができます:
#!/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のサブディレクトリのみ)と最小深さ(除く現在のフォルダとディレクトリを見つけます.
)
cd "$f"
。からの出力find
が文字列分割の場合は機能しません。そのため、名前の個別の部分がの個別の値としてあり、の展開の$f
意味を引用したり、引用したりしません$f
。
find
の出力は、デフォルトの-print
アクションを使用した行指向(1行に1つの名前を付けたものですが、理論的には-以下を参照)です。
find -print
はありません。何かを実行して、その名前に別の行として含まれるディレクトリを取得できるからです。内のファイルまたはディレクトリの名前を注意深く処理することは、権限昇格攻撃を受けるための優れた方法です。mkdir -p $'foo\n/etc/passwd\nbar'
/etc/passwd
/upload
/tmp
受け入れられた回答は、ディレクトリ名に空白がある場合は空白で中断します$()
。推奨される構文は、bash / kshです。GNU find
-exec
オプションを使用して、+;
例えば
find .... -exec mycommand +;
#this is same as passing to xargs
または、whileループを使用する
find .... | while read -r D
do
...
done
find -exec
とに渡すxargs
:find
しばらくは、実行中の命令の終了値を無視しxargs
、ゼロ以外の終了に失敗しますが。ニーズによっては、どちらが正しいかもしれません。
find ... -print0 | while IFS= read -r d
より安全です-空白で始まるまたは終わる名前、および改行リテラルを含む名前をサポートします。
mkdir 'foo * bar'
するとfoo
、原因とbar
なる場合でも繰り返される)それらは存在せず、*
はすべてのファイル名のリストに置き換えられます。ディレクトリ以外のものも含まれます)。