Bashプログラミング
 Computer >> コンピューター >  >> プログラミング >> Bashプログラミング

Bashでループを書く方法:forループの基礎からfindコマンド活用まで

Unixシェルを学びたい理由としてよく挙げられるのが、バッチ処理の力を手に入れることです。多数のファイルに対して一連の操作を実行したい場合、その方法の一つが、ファイルを順番に処理するコマンドを作ることです。プログラミング用語では、これを「実行制御」と呼び、最も代表的な例の一つがforループです。

forループとは、指定した各データオブジェクト(ファイルなど)に対して、コンピュータに実行させたい処理を記述した「レシピ」のようなものです。

定番のforループ

まず試しやすいのは、ファイルのコレクションを解析するループです。それ自体はあまり実用的ではありませんが、ディレクトリ内の各ファイルを個別に扱えることを確認するには安全で効果的な方法です。まず、テスト環境としてディレクトリを作成し、いくつかのファイルのコピーを配置しましょう。最初はどんなファイルでも構いませんが、後の例では画像ファイル(JPEGやPNGなど)が必要になります。ファイルマネージャーでもターミナルでも作成できます:

$ mkdir example
$ cp ~/Pictures/vacation/*.{png,jpg} example

新しいフォルダに移動して、ファイルを一覧表示し、テスト環境が期待どおりであることを確認します:

$ cd example
$ ls -1
cat.jpg
design_maori.png
otago.jpg
waterfall.png

各ファイルを個別にループ処理する構文は次のとおりです。まず変数を作成します(例えば、fileの頭文字を取ってf)。次に、その変数が巡回するデータセットを定義します。ここでは、「*」ワイルドカードを使って現在のディレクトリ内のすべてのファイルを巡回させます(「*」ワイルドカードはすべてに一致します)。そして、この導入部をセミコロン(;)で終了します。

$ for f in * ;

ここでEnterキーを押しても構いません。シェルは構文的に完全になるまでループを実行しようとしません。

次に、ループの各反復で何を行うかを定義します。簡単のため、fileコマンドを使って各ファイルの情報を取得してみましょう。各ファイルは変数fで表されます(先頭に$を付けることで、シェルに変数の値を展開するよう指示します):

do file $f ;

もう一度セミコロンで節を終了し、ループを閉じます:

done

Enterキーを押すと、シェルが現在のディレクトリ内のすべてを順番に処理し始めます。forループは各ファイルを1つずつ変数fに代入し、コマンドを実行します:

$ for f in * ; do
> file $f ;
> done
cat.jpg: JPEG image data, EXIF standard 2.2
design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
otago.jpg: JPEG image data, EXIF standard 2.2
waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced

次のように1行で書くこともできます:

$ for f in *; do file $f; done
cat.jpg: JPEG image data, EXIF standard 2.2
design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
otago.jpg: JPEG image data, EXIF standard 2.2
waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced

複数行形式と単一行形式は、シェルにとってはまったく同じものであり、結果も同一です。

実践的な例

日常的な作業でループがどのように役立つか、実践的な例を紹介します。友人に送りたい旅行写真のコレクションがあるとしましょう。写真ファイルはサイズが大きすぎて、メールで送るには不向きで、写真共有サービスへのアップロードにも不便です。小さなWeb用の写真を作成したいのですが、100枚もあるので、1枚ずつ縮小するのは時間がかかりすぎます。

まず、Linux、BSD、Macのパッケージマネージャーを使ってImageMagickをインストールします。FedoraやRHELの場合:

$ sudo dnf install ImageMagick

UbuntuやDebianの場合:

$ sudo apt install ImageMagick

BSDではportsまたはpkgsrcを、MacではHomebrewまたはMacPortsを使用します。

ImageMagickをインストールすると、写真を操作するための一連の新しいコマンドが使えるようになります。

生成するファイル用の出力先ディレクトリを作成します:

$ mkdir tmp

各写真を元のサイズの33%に縮小するには、次のループを実行します:

$ for f in * ; do convert $f -scale 33% tmp/$f ; done

その後、tmpフォルダを確認すれば、縮小された写真が保存されているはずです。

ループ内では任意の数のコマンドを使用できます。大量のファイルに対して複雑な操作が必要な場合は、ワークフロー全体をforループのdodoneの間に記述できます。たとえば、処理した各写真をWebホストの共有ディレクトリにコピーし、ローカルシステムからは削除したい場合は次のようにします:

$ for f in * ; do
    convert $f -scale 33% tmp/$f
    scp -i seth_web tmp/$f seth@example.com:~/public_html
    trash tmp/$f ;
 done

forループで処理される各ファイルに対して、コンピュータは自動的に3つのコマンドを実行します。10枚の写真をこの方法で処理すれば、30個のコマンドを省略でき、少なくともそれだけの時間も節約できます。

ループの範囲を限定する

ループは必ずしもすべてのファイルを対象にする必要はありません。サンプルディレクトリ内のJPEGファイルだけを処理したい場合は次のようにします:

$ for f in *.jpg ; do convert $f -scale 33% tmp/$f ; done
$ ls -m tmp
cat.jpg, otago.jpg

また、ファイルではなく、特定の回数だけアクションを繰り返したい場合もあります。forループの変数は与えるデータによって定義されるため、ファイルの代わりに数字を繰り返すループも作成できます:

$ for n in {0..4}; do echo $n ; done
0
1
2
3
4

さらにループを活用する

これで自分でループを作成する十分な知識が身につきました。ループに慣れるまでは、処理したいファイルのコピーに対して実行し、可能な限り、データを破損したり取り返しのつかないミス(ディレクトリ内の全ファイルを誤って同じ名前に変更して互いに上書きしてしまうなど)を防ぐ保護機能を持つコマンドを使用してください。

より高度なforループのトピックについては、このまま読み進めてください。

すべてのシェルがBashというわけではない

forキーワードはBashシェルに組み込まれています。多くの類似シェルは同じキーワードと構文を使用しますが、tcshなどの一部のシェルでは、代わりにforeachという別のキーワードを使用します。

tcshでは、構文の考え方は似ていますが、Bashよりも厳格です。以下のコードサンプルで、2行目と3行目の「foreach?」という文字列は入力しないでください。これは、ループの入力がまだ続いていることを示す二次プロンプトです。

$ foreach f (*)
foreach? file $f
foreach? end
cat.jpg: JPEG image data, EXIF standard 2.2
design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
otago.jpg: JPEG image data, EXIF standard 2.2
waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced

tcshでは、foreachendはそれぞれ独立した行に記述する必要があるため、Bashのようなシェルと違い、1行でforループを書くことはできません。

findコマンドによるループ処理

理論上は、forループ機能を持たないシェルに出会うこともありますし、追加機能のある別のコマンドを使いたい場合もあるでしょう。

findコマンドは、forループの機能を実現するもう一つの方法です。ループに含めるファイルの範囲を柔軟に定義できるほか、並列処理のオプションも提供しています。

findコマンドは、ハードドライブ上のファイルを検索するためのものです。構文はシンプルで、検索したい場所のパスを指定すると、findはすべてのファイルとディレクトリを検出します:

$ find .
.
./cat.jpg
./design_maori.png
./otago.jpg
./waterfall.png

名前の一部を指定して、検索結果を絞り込むこともできます:

$ find . -name "*jpg"
./cat.jpg
./otago.jpg

findの優れた点は、見つけた各ファイルを-execフラグを使ってループ処理に渡せることです。たとえば、サンプルディレクトリ内のPNG画像だけを縮小するには:

$ find . -name "*png" -exec convert {} -scale 33% tmp/{} \;
$ ls -m tmp
design_maori.png, waterfall.png

-exec節では、波括弧{}が、findが処理中の項目(つまり、見つかったPNGファイルが1つずつ)を表します。-exec節はセミコロンで終了する必要がありますが、Bashは通常セミコロンを自分用に解釈しようとします。バックスラッシュ(\;)でセミコロンを「エスケープ」することで、findがそのセミコロンを終端文字として認識できるようにします。

findコマンドは非常に有能ですが、時に有能すぎることがあります。たとえば、別の画像処理のために再度PNGファイルを検索すると、エラーが発生することがあります:

$ find . -name "*png" -exec convert {} -flip -flop tmp/{} \;
convert: unable to open image `tmp/./tmp/design_maori.png':
No such file or directory @ error/blob.c/OpenBlob/2643.
...

これは、findが現在のディレクトリ(.)だけでなく、以前に処理してtmpサブディレクトリに保存したPNGファイルまで検出してしまったためです。場合によっては、findに現在のディレクトリとその配下のすべてのディレクトリ(さらにその中のディレクトリも)を検索させたいことがあります。これは強力な再帰処理ツールとなります(音楽アーティストごとのディレクトリの中にアルバムのディレクトリがあり、その中に音楽ファイルが入っているような複雑なファイル構造で特に便利です)が、-maxdepthオプションで検索深度を制限できます。

現在のディレクトリ内のPNGファイルのみを検索するには(サブディレクトリを除外):

$ find . -maxdepth 1 -name "*png"

現在のディレクトリと1階層下のサブディレクトリまでを対象にするには、最大深度を1増やします:

$ find . -maxdepth 2 -name "*png"

デフォルトでは、すべてのサブディレクトリを再帰的に検索します。

ループで楽しく効率化

ループを使えば使うほど、時間と労力が節約され、より大きなタスクにも挑戦できるようになります。あなたは一人のユーザーかもしれませんが、よく設計されたループがあれば、コンピュータに面倒な作業を任せられます。

ループは他のコマンドと同じように扱い、複数のファイルに対して同じ操作を繰り返す必要があるときにすぐ使えるようにしておきましょう。同時に、ループは本格的なプログラミングへの入り口でもあります。多数のファイルに対して複雑なタスクを実行する必要がある場合は、まずワークフローを計画する時間を取りましょう。1つのファイルで目的を達成できるなら、その繰り返し可能なプロセスをforループで包むのは比較的簡単です。必要な「プログラミング」の知識は、変数の仕組みの理解と、未処理ファイルと処理済みファイルを分けて管理する整理能力だけです。少し練習すれば、あなたは「ループの書き方を知っているLinuxユーザー」へとステップアップできます。さあ、コンピュータに働いてもらいましょう!

  1. Bashのforループコマンドの使い方を徹底解説!自動化の第一歩

    仕事でも家庭でも、テクノロジーを活用して生活を楽にする最良の方法の一つが「自動化」です。スクリプトや定时実行ジョブでタスクを自動化すれば、時間・手間・ストレスを大幅に節約できます。しかし、何から始めればよいのか分からないという方も多いはず。この記事では、IT自動化の基礎となるツールであるBashのforループコマンドの使い方を、初心者にも分かりやすく解説します。 テスト環境の準備 まず必要なのは、動作するBashの環境です。BashはLinux、Unix、BSD、macOSのターミナルにおけるデフォルトシェルです(zshを使用している場合を除く)。現在どのシェルを実行しているか確認するに

  2. 初心者向けシェルスクリプト入門 – LinuxでBashスクリプトを作成・実行する方法を徹底解説

    シェルスクリプトは、Linuxにおける処理の自動化に欠かせない重要な技術です。スクリプトを使えば、一連のコマンドをファイルに記述し、まとめて実行できるようになります。同じコマンドを何度も入力する手間が省けるため、作業時間を大幅に節約できます。日々のタスクを効率的にこなすだけでなく、自動実行のスケジュールを組むことも可能です。さらに、新しいセッション起動時に特定のメッセージを表示したり、環境変数を設定したりするなど、システム起動時に実行されるスクリプトを設定することもできます。シェルスクリプトの活用範囲は非常に広いため、さっそく学んでいきましょう。この記事では、以下の内容を解説します。Bashシ