Linux 上のディレクトリを使用すると、ファイルを個別のコレクションにグループ化できます。欠点は、反復的なタスクを実行するためにディレクトリからディレクトリに移動するのが面倒になることです。それを自動化する方法は次のとおりです。
ディレクトリについてのすべて
Linux を初めて学ぶときに最初に学ぶコマンドはおそらく
ls
ですが、
cd
それほど遠くないでしょう。ディレクトリと、そのディレクトリ内を移動する方法、特にネストされたサブディレクトリを理解することは、 Linux がどのように組織されるか 、また自分の作業をファイル、ディレクトリ、およびサブディレクトリに組織する方法を理解するための基本的な部分です。
ディレクトリのツリーの概念を理解すること、およびディレクトリ間を移動する方法を理解することは、Linux の状況に慣れるにつれて通過する多くの小さなマイルストーンの 1 つです。 使用する
cd
パスを付けると、そのディレクトリに移動します。のようなショートカット
cd ~
または
cd
それだけでホームディレクトリに戻り、
cd ..
ディレクトリ ツリーの 1 つ上のレベルに移動します。単純。
ただし、ディレクトリ ツリーのすべてのディレクトリでコマンドを実行する同様に簡単な方法はありません。その機能を実現するにはさまざまな方法がありますが、その目的専用の標準 Linux コマンドはありません。
ls
などの一部のコマンドには、再帰的な操作を強制するコマンドライン オプションがあります。つまり、コマンドは 1 つのディレクトリで開始され、そのディレクトリの下にあるディレクトリ ツリー全体を系統的に処理します。
ls
の場合、それは
-R
(再帰的) オプション。
再帰をサポートしていないコマンドを使用する必要がある場合は、再帰機能を自分で提供する必要があります。その方法は次のとおりです。
ツリーコマンド
tree
コマンドは当面の作業には役立ちませんが、ディレクトリ ツリーの構造を簡単に確認できます。ターミナル ウィンドウにツリーが描画されるため、ディレクトリ ツリーを構成するディレクトリとサブディレクトリ、およびツリー内でのそれらの相対位置の概要を即座に把握できます。
tree
をインストールする必要があります。
Ubuntu では、次のように入力する必要があります。
sudo apt インストールツリー
Fedora では、次を使用します。
sudo dnf インストールツリー
Manjaro では、コマンドは次のとおりです。
sudo pacman -Sy ツリー
パラメータを指定せずに
tree
を使用すると、現在のディレクトリの下にツリーが表示されます。
木
コマンドラインで
tree
へのパスを渡すことができます。
木の仕事
-d
(ディレクトリ) オプションはファイルを除外し、ディレクトリのみを表示します。
ツリー -d 作業
これは、ディレクトリ ツリーの構造を明確に把握する最も便利な方法です。ここに示されているディレクトリ ツリーは、次の例で使用されているものです。 5 つのテキスト ファイルと 8 つのディレクトリがあります。
ls からディレクトリを横断する出力を解析しないでください
最初に考えるのは、
ls
ディレクトリ ツリーを再帰的に走査できるのであれば、
ls
を使用してそれを実行し、ディレクトリを解析して何らかのアクションを実行する他のコマンドに出力をパイプ処理すればよいのではないか、ということかもしれません。
ls
の出力を解析することは悪い習慣であると考えられています。 Linux では、あらゆる種類の奇妙な文字を含むファイル名やディレクトリ名を作成できるため、汎用的で普遍的に正しいパーサーを作成することが非常に困難になります。
これほどばかげたディレクトリ名を故意に作成することは決してないかもしれませんが、スクリプトやアプリケーションの間違いによっては作成される可能性があります。
正当ではあるが考慮が不十分なファイル名とディレクトリ名を解析すると、エラーが発生しやすくなります。
ls
の出力の解釈に依存するよりも安全で堅牢な方法が他にもあります。
find コマンドの使用
find
コマンドには
再帰機能が組み込まれており、コマンドを実行する機能もあります。これにより、強力なワンライナーを作成できるようになります。将来的に使用する可能性があるものであれば、ワンライナーをエイリアスまたはシェル関数に変えることができます。
このコマンドは、ディレクトリ ツリーを再帰的にループして、ディレクトリを検索します。ディレクトリが見つかるたびに、ディレクトリの名前が出力され、そのディレクトリ内で検索が繰り返されます。 1 つのディレクトリの検索が完了すると、そのディレクトリを終了し、その親ディレクトリで検索を再開します。
find work -type d -execdir echo "In:" {} \;
ディレクトリがリストされている順序によって、ツリー内で検索がどのように進行するかを確認できます。
tree
コマンドの出力と
find
ワンライナーの出力を比較すると、サブディレクトリのないディレクトリに到達するまで、
find
各ディレクトリとサブディレクトリを順番に検索する様子がわかります。その後、1 つ上のレベルに戻り、そのレベルで検索を再開します。
コマンドの構成は次のとおりです。
-
find
:
findコマンド。 - work : 検索を開始するディレクトリ。パスを指定できます。
- -type d : ディレクトリを探しています。
- -execdir : 見つかった各ディレクトリでコマンドを実行します。
- echo “In:” {} : これはコマンドです。単にディレクトリの名前をターミナル ウィンドウにエコーしているだけです。 「{}」は現在のディレクトリの名前を保持します。
- \; : コマンドの終了に使用するセミコロンです。 Bash が直接解釈しないように、バックスラッシュを使用してエスケープする必要があります。
わずかな変更を加えることで、find コマンドが検索手がかりに一致するファイルを返すようにすることができます。 -name オプションと検索手がかりを含める必要があります。この例では、「*.txt」に一致するテキスト ファイルを検索し、その名前をターミナル ウィンドウにエコー表示します。
find work -name "*.txt" -type f -execdir echo "見つかった:" {} \;
ファイルを検索するかディレクトリを検索するかは、目的によって異なります。各ディレクトリ内でコマンドを実行するには、
-type d
を使用します。一致する各ファイルに対してコマンドを実行するには、
-type f
を使用します。
このコマンドは、開始ディレクトリとサブディレクトリ内のすべてのテキスト ファイルの行数をカウントします。
find work -name "*.txt" -type f -execdir wc -l {} \;
スクリプトを使用したディレクトリ ツリーの走査
スクリプト内のディレクトリを移動する必要がある場合は、スクリプト内で
find
コマンドを使用できます。再帰的検索を自分で行う必要がある場合、または単に行いたい場合は、それも行うことができます。
#!/bin/bash
shopt -s dotglob nullglob
関数再帰 {
ローカルの current_dir dir_or_file
$1 の current_dir の場合。する
echo “次のディレクトリ コマンド:” $current_dir
“$current_dir”/* の dir_or_file の場合;する
if [[ -d $dir_or_file ]];それから
再帰的な「$dir_or_file」
それ以外
トイレ $dir_or_file
フィ
終わり
終わり
}
再帰的な「$1」
テキストをエディタにコピーし、「recurse.sh」として保存し、
chmod
コマンド
を使用して実行可能にします。
chmod +x recurse.sh
このスクリプトは、
dotglob
と
nullglob
という 2 つのシェル オプションを設定します。
dotglob
設定は、ワイルドカード検索語が展開されたときに、ピリオド「 . 」で始まるファイル名とディレクトリ名が返されることを意味します
.
これは事実上、検索結果に
隠しファイルやディレクトリ
が含まれていることを意味します。
nullglob
設定は、結果が見つからない検索パターンが空または null 文字列として扱われることを意味します。デフォルトでは検索語自体が使用されるわけではありません。つまり、アスタリスク ワイルドカード「
*
」を使用してディレクトリ内のすべてを検索しても結果がなかった場合、アスタリスクを含む文字列の代わりに null 文字列が返されます。これにより、スクリプトが誤って「*」というディレクトリを開こうとしたり、「*」をファイル名として扱ったりすることがなくなります。
次に、
recursive
という関数を定義します。ここで興味深いことが起こります。
current_dir
と
dir_or_file
という 2 つの
変数
が宣言されています。これらはローカル変数であり、関数内でのみ参照できます。
$1
という変数も関数内で使用されます。これは、関数が呼び出されたときに関数に渡される最初 (そして唯一) のパラメーターです。
このスクリプトは 2 つの
for
ループ
を使用し、1 つはもう 1 つの内部にネストされています。最初の (外側の)
for
ループは 2 つの目的で使用されます。
1 つは、各ディレクトリで実行したいコマンドを実行することです。ここで行っているのは、ディレクトリの名前をターミナル ウィンドウにエコーすることだけです。もちろん、任意のコマンドまたは一連のコマンドを使用したり、別のスクリプト関数を呼び出したりすることもできます。
外側の for ループが 2 番目に行うことは、見つかったすべてのファイル システム オブジェクト (ファイルまたはディレクトリのいずれか) をチェックすることです。これが内部
for
ループの目的です。次に、各ファイル名またはディレクトリ名が
dir_or_file
変数に渡されます。
次に、if ステートメントで
dir_or_file
変数がテストされ、ディレクトリであるかどうかが確認されます。
- 存在する場合、関数はそれ自体を呼び出し、ディレクトリの名前をパラメータとして渡します。
-
dir_or_file変数がディレクトリではない場合は、ファイルである必要があります。ファイルに適用したいコマンドは、ifステートメントのelse節から呼び出すことができます。同じスクリプト内で別の関数を呼び出すこともできます。
スクリプトの最後の行は
recursive
関数を呼び出し、最初のコマンド ライン パラメーター
$1
を検索の開始ディレクトリとして渡します。これがプロセス全体の始まりです。
スクリプトを実行してみましょう。
./recurse.sh の動作
ディレクトリが走査され、各ディレクトリでコマンドが実行されるスクリプト内のポイントが「Directory command for:」行で示されます。見つかったファイルに対して
wc
コマンドが実行され、行数、単語数、文字数がカウントされます。
最初に処理されるディレクトリは「work」で、その後にツリーのネストされた各ディレクトリ ブランチが処理されます。
注意すべき興味深い点は、ディレクトリ固有のコマンドを内部 for ループの上から下に移動することで、ディレクトリが処理される順序を変更できることです。
「Directory command for:」行を内側の
for
ループの
done
後に移動しましょう。
#!/bin/bash
shopt -s dotglob nullglob
関数再帰 {
ローカルの current_dir dir_or_file
$1 の current_dir の場合。する
“$current_dir”/* の dir_or_file の場合;する
if [[ -d $dir_or_file ]];それから
再帰的な「$dir_or_file」
それ以外
トイレ $dir_or_file
フィ
終わり
echo “次のディレクトリ コマンド:” $current_dir
終わり
}
再帰的な「$1」
ここで、スクリプトをもう一度実行します。
./recurse.sh の動作
今回は、ディレクトリには最初に最も深いレベルからコマンドが適用され、ツリーのブランチを遡って作業が行われます。パラメータとしてスクリプトに渡されたディレクトリは最後に処理されます。
より深いディレクトリを最初に処理することが重要な場合は、次の方法で実行できます。
再帰は奇妙です
それは、自分の携帯電話で自分に電話をかけ、次に会ったときに自分に伝えるメッセージを自分に残すのと同じことです。それを繰り返します。
その利点を理解するまでに多少の努力が必要になるかもしれませんが、理解できれば、それが難しい問題に取り組むためのプログラム的にエレガントな方法であることがわかります。





