サブディレクトリ Bash のファイル数を報告する

サブディレクトリ Bash のファイル数を報告する

私はWin10コンピュータで作業していますが、通常はGitbashまたはLinuxサブシステムで作業しています。

指定されたディレクトリのすべてのサブディレクトリにあるファイル数を取得しようとしています。

同様の質問です。すべてのサブディレクトリのファイル数を報告する方法は?ただし、違いは、すべてのサブディレクトリにわたって一定数のレベルを持つのではなく、次のようなものがあることです。

Dir1/sub1
Dir1/sub1/subsub1
Dir1/sub2
Dir1/sub3/subsub3/subsubsub3

頑張った

 shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done

検索するレベル数の調整(*/、*/*/*など)

しかし、実際に欲しいものを得ることはできません。たとえば、次のようになります。

Dir1/sub1: Number of files
Dir1/sub2: Number of files
Dir1/sub3: Number of files

答え1

#!/bin/bash

shopt -s dotglob nullglob

topdir='./Dir1'

for subdir in "$topdir"/*/; do
    find "$subdir" -type f -exec echo . \; |
    printf '%s: %d\n' "${subdir%/}" "$( wc -l )"
done

この小さなbashスクリプトは、サブディレクトリのパス名のリスト$topdirと、各サブディレクトリ(どこでも)で見つかった一般的なファイルの数を出力します。

このスクリプトはすべてのサブディレクトリを繰り返し、$topdir各サブディレクトリに対してfindコマンドを実行します。

find "$subdir" -type f -exec echo . \;

で見つかった通常のファイルごとに空白行に点が出力されます$subdir。計算が簡単なので、ポイントを出力します(ファイル名には改行文字を含めることができます)。

このポイントは次に接続されます。

printf '%s: %d\n' "${subdir%/}" "$( wc -l )"

ここでは、printf出力形式を指定するために使用されます。サブディレクトリパス(最後のスラッシュを削除)とファイル数を使用します。

ファイル数はwc -lパイプのポイントを計算しますfind(厳密に言えば、ポイントは計算せずに改行を計算します)。printf標準入力ストリーム自体を読み取らないので消費されますwc -l

最初とnullglobシェルdotglobオプションを設定すると、サブディレクトリなしで$topdir(withなど)、ループ全体をスキップでき、nullglob下に隠されたディレクトリ名も含めることができます(withなど)。$topdirdotglob

変更して

topdir='./Dir1'

入力する

topdir=$1

スクリプトにディレクトリパスを唯一のコマンドライン引数として使用させることができます。

findもう少し複雑なものに変更すると、速度を大幅に向上させることができます。

find "$subdir" -type f -exec sh -c 'for pathname do echo .; done' sh {} +

(残りのループはそのままにしておく必要があります。)これは、echo各ファイルではなく、見つかったファイルの配置に対して非常に小さなインラインシェルスクリプトを実行します。これは〜になりますたくさんechoFasterはシェルに組み込まれたコマンドと見なされますsh。 (これを保証するには、sh -cに変更する必要があります。)を使用すると、各ファイルに対してゆっくりと実行されます。bash -c-exec echo . \;find/bin/echo

答え2

GNUユーティリティの使用:

find Dir1 -mindepth 2 -type f -printf '%P\0' |
  awk -F/ -vRS='\0' '{n[$1]++}; END{for (i in n) print i ": " n[i]}'

カウントのみ定期的な各サブディレクトリのファイルDir1

出力は次のようになります。

sub1: 3
sub2: 30
sub3: 13
sub4: 3
sub5: 3

答え3

私はWindowsのGitbashに精通していませんが、このスクリプトを実行しているプラ​​ットフォームが何であれ、次のものがインストールされているとします。

  • bashv4.x以降(macOSユーザーは、次のものをインストールして最新バージョンをインストールする必要があります。)自分で作ったまたは他のもの)
  • GNU -find実際にはすべての標準のUnixはfind可能ですが、MS-DOS / Windowsのバージョンはそうではありませんgrep

上記を想定すると、このスクリプトはトリックを実行する必要があります。

#!/bin/bash
# USAGE: count_files <dir> ...

declare -A filecount

# Tell bash to execute the last pipeline element in this shell, not a subshell
shopt -s lastpipe

# Run through all the user-supplied directories at one go
for d in "$@"; do
  find "$d" -type f | while read f; do
    [[ $f =~ ^(${d%%/}/[^/]+)/ ]] && (( filecount["${BASH_REMATCH[1]}"]++ ))
  done
done

# REPORT!
for k in "${!filecount[@]}"; do
  echo "$k: ${filecount[$k]}"
done

答え4

バージョンが4.0以上であると仮定すると、bashほとんどそこにあります。

シェルオプションを使用すると、コード内のファイル数を再帰的に計算できますglobstar。からman bash(1)

**設定されている場合、パス名拡張コンテキストで使用されるパターンは、すべてのファイルとゼロ以上のディレクトリとサブディレクトリと一致します。パターンの後に行くと、/ディレクトリとサブディレクトリのみが一致します。

最上位ディレクトリ(サブディレクトリを含む)のすべてのファイルを再帰的に計算するには、次の手順を実行します。

shopt -s dotglob globstar
for dir in */; do
    all=( "$dir"/** )
    printf '%s\n' "$dir: ${#all[@]}"
done

試しているコードと同様に、各最上位ディレクトリのパス名拡張の結果として配列を入力し、その要素の数を表示します。名前が(隠しファイル)で始まる
dotglobファイルを含めるために使用されます。.

サブディレクトリオブジェクトを除くすべてのファイルを再帰的に計算するには、すべてのファイル数からサブディレクトリの数を減算します。

shopt -s dotglob globstar
for dir in */; do
    all=( "$dir"/** )
    alldir=( "$dir"/**/ )
    printf '%s\n' "$dir: $(( ${#all[@]} - ${#alldir[@]} ))"
done

しかし、ここでは「文書」の広範な定義を想定している。POSIXではは、通常のファイル、文字、ブロック、またはFIFO特殊ファイル、シンボリックリンク、ソケット、ディレクトリ、または標準の外部にある可能性がある特定の実装を参照できます。
特定の種類のファイル(一般ファイルなど)のみを計算するにはfind
または、上記のコードを展開してループ内のファイル形式をテストできます。

shopt -s dotglob globstar
for dir in */; do
    all=( "$dir"/** )
    count=0
    for file in "${all[@]}"; do
        test -f "$file" && count="$(( "$count" + 1 ))"
    done
    printf '%s\n' "$dir: $count"
done

ただし、このあまり便利ではないソリューションは、ベースの選択肢よりもはるかに遅くfindなります(たとえば、高速ソリューションよりも2倍以上遅い)。コサロナンダの答えbash、Linux 5.0および4.6でテスト済みfind)。

さらに、findデフォルトの動作とは異なり、このglobstarオプションを使用するパス名拡張はファイルをチェックするシンボリックリンクに従い、上記のすべてのフラグメントにもそのファイルが含まれます。
(もともとディレクトリで確認されるシンボリックリンクも使用しましたが、この動作はbash4.3で変更されました。)

最後に、シェルオプションに依存しないソリューションを提供するために、再帰globstar関数を使用してディレクトリの最上位サブディレクトリにあるすべての一般ファイルを再帰的に計算できます$1

#!/bin/bash

# nullglob is needed to avoid the function being
# invoked on 'dir/*' when * matches nothing
shopt -s nullglob dotglob

function count_files () {
    for file in "$1"/*; do
        # Only count regular files
        [ -f "$file" ] && count="$(( "$count" + 1 ))"
        # Only recurse on directories
        [ -d "$file" ] && count_files "$file"
    done
}

for dir in "$1"/*/; do
    count="0"
    count_files "$dir"
    printf '%s: %s\n' "$dir" "$count"
done

関連情報