1. ホーム
  2. bash

[解決済み] カレントディレクトリにあるすべてのファイルを再帰的に展開する方法は?

2023-01-04 08:41:41

質問

私は知っています。 **/*.ext にマッチするすべてのサブディレクトリ内のすべてのファイルに展開されます。 *.ext にマッチするすべてのサブディレクトリにあるすべてのファイルに展開されますが、同様の展開で 現在の ディレクトリにあるそのようなファイルをすべて含むような、同様の展開は何かありますか?

どのように解決するのですか?

Bash 4で動作します。

ls -l {,**/}*.ext

ダブルアスタリスクグロブを動作させるために globstar オプションが設定されている必要があります(デフォルト:on)。

shopt -s globstar

から man bash :

    グロブスター
                  設定された場合、ファイル名展開のコンマで使われるパターン**は、ファイルと0以上のディレクトリと
                  は、ファイルおよび0個以上のディレクトリとサブディレクトリに一致します。
                  サブディレクトリにマッチします。 パターンの後に / が続く場合、ディレクトリとサブディレクトリにのみ一致します。
                  ディレクトリとサブディレクトリにのみマッチします。

今、私はかつてglobstar処理にバグがあったのではないかと思っています。なぜなら、今、単に ls **/*.ext を使うと正しい結果が得られるようになりました。

にかかわらず、私が見たのは 解析 kenorb が VLC リポジトリを使用して行った解析を調べ、その解析とすぐ上の私の回答でいくつかの問題を発見しました。

の出力との比較は find コマンドの出力との比較は無効です。 -type f を指定しても他のファイルタイプ (特にディレクトリ) は含まれず、また ls コマンドはそうである可能性が高いです。また、記載されているコマンドの1つである ls -1 {,**/}*.* - というコマンドは、上記の私のものに基づいていると思われますが、出力されるのは、名前 を含む名前 を出力するだけです。OP の質問と私の回答は、求められているのが特定の拡張子を持つファイルであるため、ドットを含んでいます。

しかし、最も重要なことは、"em" を使用する特別な問題があることです。 ls コマンドとグロブスターパターン ** . このパターンは Bash によって検査対象のツリー内のすべてのファイル名(およびディレクトリ名)に展開されるため、多くの重複が発生します。展開された後に ls コマンドは とその内容(ディレクトリの場合)を表示します。

カレントディレクトリにあるサブディレクトリ A とそのコンテンツがあります。

A
└── AB
    └── ABC
        ├── ABC1
        ├── ABC2
        └── ABCD
            └── ABCD1

そのツリーの中で ** は "A A/AB A/AB/ABC A/AB/ABC/ABC1 A/AB/ABC/ABC2 A/AB/ABC/ABCD A/AB/ABC/ABCD/ABCD1" (7 entries) に展開されます。もし、あなたが echo ** とすると、これが正確な出力となり、各エントリーが一度だけ表現されます。 しかし を実行すると ls ** のリストが出力されます。 それぞれ を指定します。つまり、基本的には次のようになります。 ls A に続いて ls A/AB などとなるため A/AB は2回表示されます。また ls は各サブディレクトリの出力を別々に設定することになります。

...
<blank line>
directory name:
content-item
content-item

そのため wc -l を使うと、空白行とディレクトリ名のセクションの見出しをすべてカウントしてしまい、カウントがさらに狂ってしまいます。

これはまた別の理由で、あなたが パース ls .

このようにさらに分析した結果、この方法でファイルのツリーを反復処理する以外の状況では、globstarパターンを使用しないことをお勧めします。

for entry in **
do
    something "$entry"
done

最後の比較として、手元にあったBashのソースリポジトリを利用して、こんなことをしてみました。

shopt -s globstar dotglob
diff <(echo ** | tr ' ' '\n') <(find . | sed 's|\./||' | sort)
0a1
> .

私は tr を使って空白を改行に変えていますが、これは名前に空白が含まれていないため、ここでのみ有効です。私は sed を使って、先頭の ./ から出力される各行から find . の出力をソートしてみました。 find の出力は通常ソートされておらず、Bashのグロブの展開はすでにソートされているからです。見ての通り diff はカレントディレクトリ . が出力する find . としたとき ls ** | wc -l を実行すると、出力はほぼ2倍の行数になりました。