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


24

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

directoryName1 numberOfFiles
directoryName2 numberOfFiles

findBashが行うのに、なぜ使用したいのですか?(shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done):すべてのディレクトリについて、そのディレクトリ内のエントリの数をカウントします(隠しドットファイルを含む、.およびを除く..
-janmoesen

@janmoesenなぜあなたはそれを答えなかったのですか?私はシェルスクリプトを初めて使用しますが、メソッドに問題はありません。私には、それが最良の方法のように見えます。誰もあなたのコメントに賛成していませんが、それがなぜ悪いのかもコメントしていません。支持された答えはあなたよりもはるかに多くの担当者を持っているので、私は何かを逃しているのではないかと思います。
トキサロット14年

@toxalot:私は答えとしてそれを追加することを気にしませんでした。コメントを自由に投票してください。:-)また、「いくつのファイル」が何を意味するのかという点に関して、質問はやや曖昧です。私のソリューションは「通常の」ファイルディレクトリをカウントします。たぶん、ポスターは本当に「ディレクトリではなくファイル」を意味していた。もう1つ注意すべきことは、このグロビングでは「隠された」ドットファイルが考慮されないことです。ただし、これらの落とし穴の両方に対処する方法があります。ただし、元のポスターの正確な要件が不明です。
ジャンモエセン

回答:


30

これは、安全でポータブルな方法で行われます。奇妙なファイル名で混乱することはありません。

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、ファイル名が含まれて-testfindあなたはそれを与えたオプションに文句を言うでしょう。これを回避する方法./は、名前の前に使用することです。このように、それは同じ意味を持ちますが、もはやダッシュで始まりません。

改行とスペースも問題です。$fファイル名として「hello、buddy」が含まれている場合find ./$f、はfind ./hello, buddyです。あなたは言っているfindを見て./hello,buddy。それらが存在しない場合、文句を言い、決して覗き込むことはありません./hello, buddy。これは簡単に回避できます-変数を引用符で囲みます。

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


1
ファイル名に改行を使用する人が世界中にいるのはなぜですか?答えてくれてありがとう。
ShyBoy

1
ファイル名には、/とヌル文字以外の任意の文字を含めることができます。dwheeler.com/essays/fixing-unix-linux-filenames.html
Flimm

2
カウントにはディレクトリ自体が含まれます。あなたは、カウント、使用からそれを除外したい場合は-mindepth 1
toxalot

-printf '\n'代わりに使用することもできます-exec echo
トキサロット14年

1
@toxalotは、をサポートする検索があれば可能ですが、-printfたとえばFreeBSDで動作させたい場合はできません。
ショーンJ.ゴフ14年

6

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

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

find指定された2つのサブディレクトリをトラバースする場所-maxdepth。1に設定すると、さらなる再帰が防止-type fされ、改行で区切られたファイル()のみが報告されます。次に、結果がパイプされwcて、それらの行の数がカウントされます。


2つ以上のディレクトリがあります...コマンドとfind . -maxdepth 1 -type d出力をどのように組み合わせることができますか?
ShyBoy

(a)必要なディレクトリを変数に含めるfind $dirs ...か、または(b)それらが1つ上のレベルのディレクトリのみにある場合、そのディレクトリからのグロブのいずれかfind */ ...
jasonwryan

1
ファイル名に改行文字が含まれている場合、これは誤った結果を報告します。
ショーンJ.ゴフ

@ショーン:ありがとう。スペースを含むファイル名があると思っていましたが、改行を考慮していませんでした。修正の提案はありますか?
jasonwryan

-exec echofindコマンドに追加します-ファイル名をエコーせず、改行のみをエコーし​​ます。
ショーンJ.ゴフ

5

「再帰なし」とは、次の場合 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つの機能を実行する上記のグロブのいずれかと一致するエントリが通常のファイルかどうかをテストし、エントリが一致したかどうかをテストします(グロブのいずれかが何も一致しない場合、パターンはそのまま残ります¹)。あなたは関係なく、自分の型の指定されたディレクトリ内のすべてのエントリをカウントしたい場合は、交換してください-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)。


2

countスクリプトShawnの答え、および改行を含むファイル名でも使用可能な形式で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' vsをprintf %q $'\tfoo\nbar'

このfindコマンドは、ファイルごとに単一の文字を出力し、行をカウントする代わりにそれらをカウントするだけで機能します。


1

ここで使用して、あなたの結果を得るために、「ブルートフォース」-ish方法だfindecholswcxargsawk

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

この作品。しかし、名前に ``スペースがあるディレクトリがある場合、出力は台無しになります。
ShyBoy

ファイル名に改行文字が含まれている場合、これは誤った結果を報告します。
ショーンJ.ゴフ

-1
for i in *; do echo $i; ls $i | wc -l; done

4
U&Lへようこそ。回答は、単なるコードドロップではなく、説明付きの長い形式である必要があります。これを展開して、何が起こっているのか説明してください。また、これはこれを行うには非常に非効率的な方法であり、たとえばスペースを含むファイルを処理しません。
slm

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.