1. 序章

このチュートリアルでは、複数のディレクトリで同じコマンドを実行するためのいくつかのBashアプローチについて説明します。

2. よく見る

まず、目標を小さなステップに分割しましょう

  1. 現在のフォルダの内容を一覧表示します
  2. ハードリンクサブフォルダではないものをすべて除外します
  3. サブフォルダごとにコマンドを実行します
  4. サブフォルダごとに、手順1に戻ります。

ロジックは単純に見えますが、特に実行するコマンドが元に戻せない場合は、ステップ2が実際には非常に重要です。

実際、ステップ2がないと、タッチするつもりのないファイルに対して、またはシンボリックリンクが原因で予期しない場所で、潜在的に危険なコマンドを実行してしまう可能性があります。

選択したアプローチに応じて、これらの問題にどのように対処できるかを示します。

3. テスト環境を準備する

そして、実際の問題解決を掘り下げる前に、環境を準備しましょう

# create some folders
for folder in 1 2 3
do
    mkdir folder_$folder
done

# create a sub-directory
mkdir folder_1/sub_folder

# create an empty file
touch my_file

# create a symbolic link pointint to folder_3
ln -s folder_3 my_symbolik_link_to_3

私たちが作成したものを確認しましょう:

tree

.
├── folder_1
│   └── sub_folder
├── folder_2
├── folder_3
├── my_file
└── my_symbolik_link_to_3 -> folder_3

5 directories, 1 file

これで、スクリプトの準備が整いました…

4. ループ

Bashでは、ループはさまざまな組み込みを使用してプログラムできます。 このセクションでは、それらを1つずつ調べていきます。

いずれかの時点で彼らのmanページを見る必要がある場合は、組み込みの命令がメインのbash manページにあることを覚えておく必要があるため、それらにアクセスするにはmanbashを実行する必要があります。ターミナルでを選択し、ページで組み込みキーワードを検索します( for while 、または until )。

セクション2で説明したロジックを実装するために、2つのテスト条件を使用します。

  • -d 。考慮されるパスがディレクトリの場合、trueを返します。
  • -h。考慮されるパスがシンボリックリンクの場合、trueを返します。

-d はフォルダーを指すシンボリックリンクを除外しないため、両方の条件が必要です。

4.1. forループ

forループは、その範囲構文を使用して現在のフォルダーコンテンツを簡単に取得し、各アイテムをループできるため、非常に便利です。

function recursive_for_loop { 
    for f in *;  do 
        if [ -d $f  -a ! -h $f ];  
        then  
            cd -- "$f";  
            echo "Doing something in folder `pwd`/$f"; 

            # use recursion to navigate the entire tree
            recursive_for_loop;
            cd ..; 
        fi;  
    done;  
};
recursive_for_loop

上記のコードは、前述の両方のフィルターを適用します。 その結果、ファイルまたはシンボリックリンクはコードによって処理されません

# Result
Doing something in folder /home/user/workspace/folder_1
Doing something in folder /home/user/workspace/folder_1/sub_folder
Doing something in folder /home/user/workspace/folder_2
Doing something in folder /home/user/workspace/folder_3

前に定義した条件によって、パスに存在するファイルとシンボリックリンクの両方が正常に除外されたことがわかります。

4.2. whileループ

while の場合、範囲から直接読み取ることができないため、代わりに別のコマンドの出力をパイプする必要があります。

function recursive_for_loop { 
    ls -1| while read f; do
        if [ -d $f  -a ! -h $f ];  
        then  
            cd -- "$f";  
            echo "Doing something in folder `pwd`/$f"; 

            # use recursion to navigate the entire tree
            recursive_for_loop;
            cd ..; 
        fi;  
    done;  
};
recursive_for_loop

# Result
Doing something in folder /home/user/workspace/folder_1
Doing something in folder /home/user/workspace/folder_1/sub_folder
Doing something in folder /home/user/workspace/folder_2
Doing something in folder /home/user/workspace/folder_3

4.3. からループまで

until構文は同じ手法を使用してフォルダーのリストを読み取りますが、ループ条件で否定が必要です。

これは、ロジックが異なるためです。whileは条件がtrueの場合にループ命令を実行し、untilは条件がfalseの場合にループ命令を実行します。

function recursive_for_loop { 
    ls -1| until ! read f; do
        if [ -d $f  -a ! -h $f ];  
        then  
            cd -- "$f";  
            echo "Doing something in folder `pwd`/$f"; 

            # use recursion to navigate the entire tree
            recursive_for_loop;
            cd ..; 
        fi;  
    done; 
};
recursive_for_loop

# Result
Doing something in folder /home/user/workspace/folder_1
Doing something in folder /home/user/workspace/folder_1/sub_folder
Doing something in folder /home/user/workspace/folder_2
Doing something in folder /home/user/workspace/folder_3

5. findコマンド

ループの代わりに、 find コマンドがあります。これは、ディレクトリ階層内のファイルを検索することを主な目的としています。

Linux 最近変更されたファイルを検索する記事で、findを使用して最近変更されたファイルを検索する方法を説明しました。

この例では、-exec-execdirの2つのオプションを検討します。どちらも、一致した各ファイルで指定されたコマンドを実行するという同じ目的を持っています。

同じ結果が得られますが、 -execdir オプションは、一致したファイル(またはこの場合はサブディレクトリ)が存在するディレクトリ内からコマンドを実行するため、より安全であると見なされ、競合状態を回避できます。

それでも危険があります。execdirはフォルダーに入った後にコマンドを実行するため、コマンドと同じ名前の実行可能ファイルが含まれている場合、findはローカルコマンドの代わりにローカルコマンドを実行します。私たちが意図したもの。

しかし、私たちの単純なケースのシナリオでは、違いはありません。

この最後の点を証明するために、-execオプションを指定してfindを実行してみましょう。

find ./* -type d -exec touch {}/test \;

# Result
 tree
.
├── folder_1
│   ├── sub_folder
│   │   └── test
│   └── test
├── folder_2
│   └── test
├── folder_3
│   └── test
├── my_file
└── my_symbolik_link_to_3 -> folder_3

このコマンドは、各サブディレクトリに「テスト」ファイルを正常に生成しました。

次の手順に進む前に、作成したばかりのテストファイルを削除しましょう。

記事Linuxコマンド–Xより古いファイルの削除のセクション2.4ですでに紹介されているのと同じスクリプトを使用できます。

find . -type f -name test -exec rm -i {} \;

# Result
tree
.
├── folder_1
│   └── sub_folder
├── folder_2
├── folder_3
├── my_file
└── my_symbolik_link_to_3 -> folder_3

それでは、-execdirオプションを指定してfindを試してみましょう。

find ./* -type d -execdir touch {}/test \;

# Result
tree
.
├── folder_1
│   ├── sub_folder
│   │   └── test
│   └── test
├── folder_2
│   └── test
├── folder_3
│   └── test
├── my_file
└── my_symbolik_link_to_3 -> folder_3

したがって、両方のオプションが私たちのケースシナリオで同じ結果をもたらすことを証明しました。

find をループと比較すると、フィルタリング条件を使用する必要がないことがわかります。オプション-type d は、ディレクトリではないものをすべて除外し、デフォルトではコマンドシンボリックリンクをたどりません。

複数のコマンドを実行する場合は、同じオプションを複数回繰り返す必要があります。

find ./* -type d -execdir echo Doing something in folder {} \; -execdir echo Done something in {} \;

# Result
Doing something in folder ./folder_1
Done something in ./folder_1
Doing something in folder ./sub_folder
Done something in ./sub_folder
Doing something in folder ./folder_2
Done something in ./folder_2
Doing something in folder ./folder_3
Done something in ./folder_3

6. xargsコマンド

xargs コマンドは、標準入力を使用してコマンドラインを構築および実行します。

次に、 find の出力をパイプ処理して、見つかった各ディレクトリで必要なコマンドを実行できます。

find ./* -type d | xargs -I {} echo Doing something in folder {}

# Result
Doing something in folder ./folder_1
Doing something in folder ./folder_1/sub_folder
Doing something in folder ./folder_2
Doing something in folder ./folder_3

7. 検索深度を制御する

上記のすべてのケースでは、ディレクトリツリー全体をトラバースする必要があると想定していますが、開始または到達する深さを制限したい場合はどうなりますか

この目的のために、 find には、-mindepth-maxdepthの2つの便利なオプションもあります。

それらを適用するには、関心のある深度レベルを設定する必要があります。ここで、数字のゼロは、現在のディレクトリを表します。

以前と同じ動作を再現してみましょう。

find ./* -mindepth 0 -maxdepth 1 -type d -exec echo Doing something in folder {}\;

# Result
Doing something in folder ./folder_1
Doing something in folder ./folder_1/sub_folder
Doing something in folder ./folder_2
Doing something in folder ./folder_3

次に、 maxdepth を変更して、第1レベルのサブフォルダーのみを検索します。

find ./* -mindepth 0 -maxdepth 0 -type d -exec echo Doing something in folder {}\;

# Result
Doing something in folder ./folder_1
Doing something in folder ./folder_2
Doing something in folder ./folder_3

sub_folderでコマンドが実行されていないことがわかります。

それでは、代わりに mindepth を変更して、ツリーの2番目のレベルでのみコマンドを実行してみましょう。

find ./* -mindepth 1 -maxdepth 1 -type d -exec echo Doing something in folder {}\;

# Result
Doing something in folder ./folder_1/sub_folder

これまで見てきたように、これらのオプションは検索検索の制御を強化しますが、欠点として、処理したいツリー構造を正確に知る必要があります。

8. 結論

このチュートリアルでは、ローカルパスのツリーにあるすべてのサブディレクトリでBashコマンドを実行するためのさまざまなアプローチを検討し、Bashの組み込みツールと基本的なLinuxツールをテストしました。