1. 概要

Linuxコマンドラインで作業する場合、ファイルを操作する必要があることがよくあります。 ファイルの削除は一種の一般的な操作です。

ファイル削除のさまざまな要件に直面している可能性があります。たとえば、特定の時間より古いファイルを削除する特定の拡張子を持つファイルを再帰的に削除する一度に複数のファイルを削除する[ X218X]など。

今回は、別のファイルにリストされているファイルを削除する方法について説明します。

2. 問題の紹介

2.1. 問題の例

例は問題をより簡単に説明するかもしれません。

まず、ディレクトリdelTestとその下にいくつかのファイルを用意しました。

$ tree /tmp/delTest/
/tmp/delTest/
├── jpg_files
│   ├── olympic tokyo 001.jpg
│   ├── olympic tokyo 002.jpg
│   └── olympic tokyo 003.jpg
├── pdf_files
│   ├── bar.pdf
│   └── foo.pdf
├── toDelete.txt
└── txt_files
    ├── bar.txt
    └── foo.txt

3 directories, 8 files

tree コマンドを使用して、ディレクトリ構造をより明確に出力しました。 jpg_filesディレクトリの下のファイル名にはスペースが含まれていることに注意してください。

削除するファイルをtoDelete.txtファイルに定義したとします。

$ cat toDelete.txt 
/tmp/delTest/pdf_files/foo.pdf
/tmp/delTest/txt_files
/tmp/delTest/jpg_files/olympic tokyo 001.jpg

私たちの目標は、toDelete.txtファイルにリストされているファイルを削除することです。

2.2. 問題を解決するためのアイデア

toDelete.txt ファイルには、削除するファイルとディレクトリが定義されているため、問題を解決するための2つのアイデアがあります。

  • toDelete.txt ファイルの各行を読み取り、それぞれに対してrmコマンドを実行してファイルを削除します。
  • toDelete.txt ファイルの内容を読み取り、その行を一連のrmコマンドに変換します。 最後に、結果をshコマンドにパイプして実行します。

両方のアイデアをカバーするための3つのアプローチについて説明します。

  • 純粋なBashを使用する
  • xargsコマンドの使用
  • sedコマンドの使用
  • awkコマンドの使用

それでは、実際の動作を見てみましょう。

3. ピュアバッシュの使用

今日、Bashはほとんどの最新のLinuxディストリビューションのデフォルトシェルになっています。 したがって、純粋なBashで問題を解決した場合、つまり、私たちのソリューションは追加の依存関係に依存しません

3.1. ディレクトリの処理

例が示すように、問題は非常に単純に見えます。 ただし、問題にはいくつかのバリエーションがあります。 それでは、それらを詳しく見てみましょう。

toDelete.txt ファイルにリストされているファイルには、ディレクトリが含まれている場合があります。 たとえば、 / tmp / delTest /txt_filesはディレクトリです。

要件によっては、ディレクトリの削除をスキップするか、ディレクトリを再帰的に削除することもできます。

rm コマンドは、-rオプションを提供します。 このオプションを使用すると、ディレクトリを再帰的に削除できます。 また、通常のファイルでrm -rを実行すると、ファイルも削除されます

したがって、 toDelete.txtファイル内のファイルとディレクトリを削除する必要がある場合は、ファイル内の行を使用してrm-rを実行できます。 それ以外の場合は、-rオプションを指定せずにrmを呼び出して、ディレクトリの削除をスキップします

このチュートリアルでは、要件がディレクトリとファイルの削除であると想定します。 つまり、ファイル削除コマンドとしてrm-rオプションを使用します。

3.2. ファイル名の引用

ほとんどのLinuxファイルシステムはファイル名に空白を受け入れます。たとえば、ファイル / tmp / delTest / jpg_files / olympic tokyo001.jpgには複数のスペースが含まれています。

ファイルを名前で操作するときは、名前を引用することをお勧めします。 または、予期しない結果が発生する可能性があります。

ただし、ファイル名を引用符で囲むと、一重引用符を使用するか二重引用符を使用するかに関係なく、glob拡張が無効になることに注意してください。

また、globのサポートはこの問題の要件ではないと仮定しましょう。 これは、 toDelete.txt ファイルに誤りがあると、現実の世界では危険な場合があるためです。 たとえば、 / path / * / *rm -r を実行すると、かなりの数のファイルが削除されます。

したがって、すべてのソリューションでファイル名を常に引用します。

それでは、 toDelete.txt ファイルを読み取り、ファイルを削除する簡単なシェルスクリプトを作成しましょう。

3.3. シンプルなBashスクリプト

まず、スクリプトを見てみましょう。

$ cat delFromFile.sh 
#!/bin/bash

TO_BE_DEL="$1"
IFS=""

while read -r file ; do
    rm -r "$file"
done < "$TO_BE_DEL"

スクリプトを理解するのは難しくありません。 whileループのみが含まれています。 さっそく通過させましょう。

まず、 IFS 変数を空に設定して、toDelete.txtの各行が読み取り時にレコードになるようにします。

while ループでは、toDelete.txtファイルで定義された各ディレクトリまたはファイルに対してrm-rコマンドを実行します。

次に、スクリプトをテストして、期待どおりに機能するかどうかを確認しましょう。

$ ./delFromFile.sh /tmp/delTest/toDelete.txt 
$ tree /tmp/delTest
/tmp/delTest
├── jpg_files
│   ├── olympic tokyo 002.jpg
│   └── olympic tokyo 003.jpg
├── pdf_files
│   └── bar.pdf
└── toDelete.txt

2 directories, 4 files

スクリプトを実行した後、 tree コマンドを再度呼び出して、結果を確認します。 toDelete.txt。にリストされているディレクトリとファイルが正常に削除されたことがわかります。

したがって、私たちは問題を解決しました。

4. xargsコマンドの使用

xargs コマンドは、 stdin から入力を読み取り、それを引数に変換して他のコマンドにフィードします。 

次に、toDelete.txtファイルにリストされているファイルを削除するワンライナーxargsコマンドを見てみましょう。

xargs -I{} rm -r "{}" < /tmp/delTest/toDelete.txt

xargsコマンドはstdinからのみ読み取るため、入力ファイルtoDelete.txtstdinにリダイレクトします。 また、 rm コマンドでファイル名を引用できるように、プレースホルダー「{}」を定義しました。

次に、 / tmp / delTest ディレクトリの下にあるファイルを復元して、次のコマンドをテストします。

$ xargs -I{} rm -r "{}" </tmp/delTest/toDelete.txt

$ tree /tmp/delTest
/tmp/delTest/
├── jpg_files
│   ├── olympic tokyo 002.jpg
│   └── olympic tokyo 003.jpg
├── pdf_files
│   └── bar.pdf
└── toDelete.txt

2 directories, 4 files

コマンドは期待どおりに機能します。

5. sedコマンドの使用

これまで、純粋なBashとxargsを使用して問題を解決する方法を見てきました。 どちらのアプローチも、 toDelete.txt ファイルから各行を取得し、 rm-rコマンドをフィードします。

他のアイデアに従って、ファイルの内容を複数のrmコマンドに変換できます。

sed コマンドは、コンパクトなワンライナーでこの種のタスクを簡単に処理できます。

$ sed 's/.*/rm -r "\0"/' /tmp/delTest/toDelete.txt
rm -r "/tmp/delTest/pdf_files/foo.pdf"
rm -r "/tmp/delTest/txt_files"
rm -r "/tmp/delTest/jpg_files/olympic tokyo 001.jpg"

上記の出力が示すように、 sed ワンライナーは、置換によって必要なrmコマンドを構築しました。 さらに、ファイル名も適切に引用されています。

sed コマンド自体は実際の削除を実行しませんが、生成されるrmコマンドを確認する機会があります。 したがって、間違いを検出するのに役立つ場合があります。

コマンドに問題がなければ、sedワンライナーの結果をshにパイプしてファイルを削除できます。

次に、ファイルを復元して、sedワンライナーをテストしましょう。

$ sed 's/.*/rm -r "\0"/' /tmp/delTest/toDelete.txt | sh

$ tree /tmp/delTest
/tmp/delTest/
├── jpg_files
│   ├── olympic tokyo 002.jpg
│   └── olympic tokyo 003.jpg
├── pdf_files
│   └── bar.pdf
└── toDelete.txt

2 directories, 4 files

6. awkコマンドの使用

toDelete.txtファイルからrmコマンドを作成することは、awkにとって簡単なことです。 同様に、awkの置換関数を使用してジョブを実行することもできます。

ただし、ここでは、別の方法を示します。

$ awk -v q='"' '$0 = "rm -r " q $0 q' /tmp/delTest/toDelete.txt
rm -r "/tmp/delTest/pdf_files/foo.pdf"
rm -r "/tmp/delTest/txt_files"
rm -r "/tmp/delTest/jpg_files/olympic tokyo 001.jpg"

短いawkコマンドがどのように機能するかを簡単に理解しましょう。

引用符のエスケープを回避し、コードを読みやすくするために、二重引用符の文字を格納するawk変数qを宣言しました。

rm-r 」を連結して元の入力行に引用した後、結果が空の文字列であってはならないことがわかっています。

awk は、空でもゼロでもない文字列をTrueとして評価します。 また、tステートメント‘True’は、デフォルトのアクションをトリガーします。 現在の処理ラインを印刷します。 

したがって、awkは生成されたrmコマンドを出力します。

sed ソリューションと同じように、実際の削除を行う場合は、awk出力をshにパイプする必要があります。

$ awk -v q='"' '$0="rm -r " q $0 q' /tmp/delTest/toDelete.txt | sh

$ tree /tmp/delTest
/tmp/delTest/
├── jpg_files
│   ├── olympic tokyo 002.jpg
│   └── olympic tokyo 003.jpg
├── pdf_files
│   └── bar.pdf
└── toDelete.txt

2 directories, 4 files

7. 結論

この記事では、この問題を解決するための2つのアイデア、「ファイルにリストされているファイルを削除する」について説明しました。

純粋なBashおよびxargsソリューションは、ファイルから各行を読み取り、rmコマンドをフィードしてジョブを実行します。

または、sedawkなどのテキスト処理ユーティリティを使用して、複数の rm コマンドを生成し、それらをshにパイプすることもできます。 ] この問題を解決するために。