web-dev-qa-db-ja.com

すべてのサブディレクトリ内のファイル数を報告する方法は?

すべてのサブディレクトリを調べて、それらに含まれるファイル(再帰なし)の数を報告する必要があります。

directoryName1 numberOfFiles
directoryName2 numberOfFiles
25
ShyBoy

これは安全で移植可能な方法でそれを行います。奇妙なファイル名で混乱することはありません。

for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \; | wc -l && echo $f; done

最初にファイル数を出力し、次にディレクトリ名を別の行に出力することに注意してください。 OPのフォーマットを維持したい場合は、さらにフォーマットする必要があります。

for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \;|wc -l|tr '\n' ' ' && echo $f; done|awk '{print $2"\t"$1}'

関心のある特定のサブディレクトリセットがある場合は、*をそれらに置き換えることができます。

なぜこれが安全なのですか?(したがって、スクリプトに値する)

ファイル名には、/以外の任意の文字を含めることができます。シェルまたはコマンドによって特別に処理される文字がいくつかあります。それらには、スペース、改行、ダッシュが含まれます。

for f in *構文を使用すると、内容に関係なく、各ファイル名を安全に取得できます。

変数にファイル名を入れても、find $fのようなことは避けなければなりません。 $fにファイル名-testが含まれている場合、findは指定したオプションについて文句を言うでしょう。これを回避する方法は、名前の前に./を使用することです。この方法でも同じ意味ですが、ダッシュで始まっていません。

改行とスペースも問題です。 $fにファイル名として「hello、buddy」が含まれている場合、find ./$ffind ./hello, buddyです。 find./hello,buddyを見るように指示しています。それらが存在しない場合、文句を言い、./hello, buddyを検索することはありません。これは簡単に回避できます。変数を引用符で囲んでください。

最後に、ファイル名には改行を含めることができるため、ファイル名のリストで改行を数えることはできません。改行を含むすべてのファイル名に対して追加のカウントが得られます。これを回避するには、ファイルのリストの改行を数えないでください。代わりに、単一のファイルを表す改行(またはその他の文字)を数えます。これが、findコマンドに-exec echo \;ではなく-exec echo {} \;があるだけの理由です。ファイルを集計する目的で、新しい行を1行だけ印刷したいと思います。

30
Shawn J. Goff

標準のLinuxソリューションを探していると仮定すると、これを達成する比較的簡単な方法はfindを使用することです。

find dir1/ dir2/ -maxdepth 1 -type f | wc -l

ここで、findは、指定された2つのサブディレクトリを-maxdepthの1に移動します。これにより、再帰が防止され、改行で区切られたファイル(-type f)のみが報告されます。次に、結果をwcにパイプして、それらの行の数をカウントします。

6
jasonwryan

「再帰なし」とは、directoryName1にサブディレクトリがある場合、サブディレクトリ内のファイルをカウントしないことを意味しますか?もしそうなら、ここに示されたディレクトリ内のすべての通常のファイルを数える方法があります:

count=0
for d in directoryName1 directoryName2; do
  for f in "$d"/* "$d"/.[!.]* "$d"/..?*; do
    if [ -f "$f" ]; then count=$((count+1)); fi
  done
done

-fテストは2つの機能を実行することに注意してください:上記のグロブの1つに一致するエントリが通常のファイルであるかどうかをテストし、エントリが一致したかどうかをテストします(グロブの1つが何にも一致しない場合、パターンそのままです¹)。タイプに関係なく、指定されたディレクトリのすべてのエントリをカウントする場合は、-f-eに置き換えます。

Kshには、ドットファイルにパターンを一致させる方法と、ファイルがパターンに一致しない場合に空のリストを生成する方法があります。したがって、kshでは、次のような通常のファイルをカウントできます。

FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
  if [ -f "$x" ]; then ((++count)); fi
done

またはすべてのファイルは次のようになります:

FIGNORE='.?(.)'
files=(~(N)directoryName1/* ~(N)directoryName2/*)
count=${#files}

Bashには、これを簡単にするためのさまざまな方法があります。通常のファイルを数えるには:

shopt -s dotglob nullglob
count=0
for x in directoryName1/* directoryName2/*; do
  if [ -f "$x" ]; then ((++count)); fi
done

すべてのファイルを数えるには:

shopt -s dotglob nullglob
files=(directoryName1/* directoryName2/*)
count=${#files}

いつものように、zshではさらに単純です。通常のファイルを数えるには:

files=({directoryName1,directoryName2}/*(DN.))
count=$#files

すべてのファイルをカウントするには、(DN.)(DN)に変更します。

¹ 各パターンはそれ自体と一致することに注意してください。一致しない場合、結果がオフになる可能性があります(たとえば、数字で始まるファイルを数える場合、for x in [0-9]*; do if [ -f "$x" ]; then …を実行できません。これは、[0-9]fooというファイルが存在する可能性があるためです。 )。

count scriptShawn's answer 、および改行を含むファイル名でも使用可能な形式で1行に出力されるようにするBashトリックに基づいています。

for f in *
do
    if [ -d "./$f" ]
    then
        printf %q "$f"
        printf %s ' '
        find "$f" -maxdepth 1 -printf x | wc -c
    fi
done

printf %qは、引用符で囲まれたバージョンの文字列、つまりBashスクリプトに入力して、(潜在的に)改行やその他の特殊文字を含むリテラル文字列として解釈できる単一行の文字列を出力します。たとえば、echo -n $'\tfoo\nbar'printf %q $'\tfoo\nbar'をご覧ください。

findコマンドは、ファイルごとに1文字を出力し、行を数えるのではなくそれらを数えるだけで機能します。

2
l0b0

findecholswcxargsを使用して結果を取得する「ブルートフォース」っぽい方法は次のとおりですおよびawk

find . -maxdepth 1 -type d -exec sh -c "echo '{}'; ls -1 '{}' | wc -l" \; | xargs -n 2 | awk '{print $1" "$2}'
1
TheGeneral