繰り返し処理では、「for文」または「while文」を使用します。今回は、「for」による繰り返し処理を解説します。for文では、“カレントディレクトリのファイルを順次処理する”のようにリストを基に繰り返す処理や、“1から10まで”のように回数を基にした繰り返し処理を行います。
「for 変数 in リスト do ~ done」で、リストを一つ一つ変数にセットして、doからdoneの間に書かれた処理を実行します。
- for 変数 in リスト
- do
- コマンド
- done
forはコマンドラインで使用することもできます。筆者の別連載「Linux基本コマンドTips」の第215回も参考にしてください。
for文の雰囲気をつかむために、「1」「2」「3」と順番に表示するスクリプトを作ってみましょう。
- #! /bin/bash
- for cnt in 1 2 3 #1,2,3を順番にcntにセットしてdo~doneを処理する
- do
- echo $cnt #変数cntの内容を表示する
- done
- $ chmod +x showcount
- $ ./showcount
- 1
- 2
- 3
少し応用すると、以下のような実行も可能です。以下のスクリプトでは、do~doneの中で「echo」コマンドと「ping」コマンドを実行しています。
- #! /bin/bash
- for cnt in {1..5} #1,2,3,4,5を順番にcntにセットしてdo~doneを処理する
- do
- echo "check: 192.168.1.$cnt"
- ping -c 3 192.168.1.$cnt #pingを実行(「-c 3」は3回トライして終了するオプション)
- done
- $ chmod +x seqping
- $ ./seqping
- check: 192.168.1.1 ← echoコマンド
- PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data. ← pingコマンド
- 64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.974 ms
- 64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.787 ms
- 64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=0.788 ms
- --- 192.168.1.1 ping statistics ---
- 3 packets transmitted, 3 received, 0% packet loss, time 2001ms
- rtt min/avg/max/mdev = 0.787/0.849/0.974/0.094 ms
- check: 192.168.1.2 ← echoコマンド
- PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data. ← pingコマンド
- …
- #(192.168.1.1~192.168.1.5に対しechoとpingが実行される)
for文では、doの前とdoneの前に“改行“が必要です。また、if文と同様に「;」記号で改行をなくすことができます。
先ほどの「showcount」スクリプトを書き換えると、以下のようになります。どちらも内容は全く同じです。
- #! /bin/bash
- for cnt in 1 2 3; do
- echo $cnt
- done
- #! /bin/bash
- for cnt in 1 2 3; do echo $cnt; done
次に“引数を1つずつ表示する”というスクリプトを作ってみましょう。ここでは「for argv in $@」のように指定しています。
「$@」はシェルスクリプト実行時の「全ての引数」という意味で、例えば「showargv one two three」と実行した場合は、「one」「two」「three」を順番に変数argvに入れて、doとdoneの間の処理を実行します。
- #! /bin/bash
- for argv in $@ #引数を1つずつ変数argvに入れて「do~done」の間を実行する
- do
- echo $argv #変数argvの内容を表示する
- done
- $ chmod +x showargv
- $ ./showargv one two three
- one
- two
- three
- #(引数が1つずつ表示された)
先ほどのshowargvは、コマンドラインで「showargv *」のように実行すると、カレントディレクトリにあるファイルが1つずつ表示されることになります。これは、bashによって「*」部分がカレントディレクトリにある全てのファイル名に展開された上で実行されているためです。これを「パス名展開(Pathname Expansion)」と呼びます。
例えば、カレントディレクトリに「file1」「file2」「file3」があったならば、「showargv *」で「showargv file1 file2 file3」が実行されます。
- $ ls *
- file1 file2 file3
- #(カレントディレクトリにfile1、file2、file3がある)
- $ showargv *
- file1
- file2
- file3
- #(パス名展開されてshowargv file1 file2 file3が実行された)
以上を踏まえて、“指定したファイルを「ファイル名.年-月-日」にコピーする”という想定でスクリプトを作ってみましょう。
先ほどと同じ要領で「for f in "$@"」で引数を1つずつ変数fにセットし、「cp」コマンドでファイルをコピーします。ファイル名は、元のファイル名に対し「.」という文字と、「date +%Y-%m-%d」の実行結果を追加しています。
なお、今の段階では、cpコマンドの前に「echo」を付けています。「このようなコマンドが実行されるはず」というイメージで実行結果を参照してください。
- #! /bin/bash
- for filename in "$@"
- do
- echo cp "$filename" "$filename.`date +%Y-%m-%d`"
- done
- $ filebackup * ##引数に「*」を指定
- cp file1 file1.2018-12-11
- cp file2 file2.2018-12-11
- cp file3 file3.2018-12-11
- #(このようなcpコマンドが実行される予定)
- $ filebackup file1 file2 #ファイル名を個別に指定してもよい
- cp file1 file1.2018-12-11
- cp file2 file2.2018-12-11
- #(このようなcpコマンドが実行される予定)
filebackupスクリプトの「echo cp ~」部分からechoコマンドを取って「cp ~」のようにすると、cpコマンドが実行されます。
次回は、この「filebackup」スクリプトを加工して、ファイルのコピーと圧縮を行うスクリプトを作成します。また、引数で指定したファイルの有無を確認したり、指定したファイル名の中に空白があったりした場合の対策も行います。
西村 めぐみ(にしむら めぐみ)
もともとはDOSユーザーで「DOS版UNIX-like tools」を愛用。ソフトハウスに勤務し生産管理のパッケージソフトウェアの開発およびサポート業務を担当、その後ライターになる。著書に『図解でわかるLinux』『らぶらぶLinuxシリーズ』『Accessではじめるデータベース超入門[改訂2版]』『macOSコマンド入門』など。地方自治体の在宅就業支援事業にてMicrosoft Officeの教材作成およびeラーニング指導を担当。会社などの“PCヘルパー”やピンポイント研修なども行っている。
Copyright © ITmedia, Inc. All Rights Reserved.
Linux �� OSS 險倅コ九Λ繝ウ繧ュ繝ウ繧ー