私は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など)。$topdir
dotglob
変更して
topdir='./Dir1'
入力する
topdir=$1
スクリプトにディレクトリパスを唯一のコマンドライン引数として使用させることができます。
find
もう少し複雑なものに変更すると、速度を大幅に向上させることができます。
find "$subdir" -type f -exec sh -c 'for pathname do echo .; done' sh {} +
(残りのループはそのままにしておく必要があります。)これは、echo
各ファイルではなく、見つかったファイルの配置に対して非常に小さなインラインシェルスクリプトを実行します。これは〜になりますたくさんecho
Fasterはシェルに組み込まれたコマンドと見なされます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に精通していませんが、このスクリプトを実行しているプラットフォームが何であれ、次のものがインストールされているとします。
bash
v4.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
オプションを使用するパス名拡張はファイルをチェックするシンボリックリンクに従い、上記のすべてのフラグメントにもそのファイルが含まれます。
(もともとディレクトリで確認されるシンボリックリンクも使用しましたが、この動作はbash
4.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