前回に続き「for文」を解説します。今回は、if文と組み合わせてファイルをバックアップするスクリプトを作成します。
今回は、前回の最後に作成した、指定したファイルを「ファイル名.年-月-日」にコピーする「filebackup」スクリプトを加工します。
例えば、“指定したファイルを「ファイル名.年-月-日.gz」という名前で圧縮する”という内容にするならば、「cp」コマンドでファイルをコピーし、その後、「gzip」コマンドで圧縮するという流れで組み立てることができます。
cpとgzipの両方で「ファイル名.年-月-日」という名前を使用することになるので、いったん変数「backupname」に保存します。このようにしておくことで、cpとgzipで異なった名前を使用してしまうというミスを防ぐことができます。また、バックアップファイル名を変更したくなった場合でも、1箇所だけ修正すればよいので加工もしやすくなります。
ちなみに、ここではバックアップのファイル名に「年-月-日」を使っているので、同じ日に、同じファイルに対してfilebackupスクリプトを実行した場合は「ファイル名.年-月-日」が再度コピーされます(※)。
【※】同じ日に別のバックアップを取りたいという場合は「年-月-日」ではなく、時刻まで指定するとよいでしょう。この場合は「date」コマンドのオプションに「%H」(時)、「%M」(分)、「%S」(秒)を追加して、「`backupname=$filename.`date +%Y-%m-%d-%H-%M-%S`」のようにします。
そこで、gzipには「--force」オプションを付けて、圧縮ファイルが存在した場合は再作成するようにしています。「--force」は「-f」オプションと同じ意味ですが、スクリプト中なので、意味が分かりやすいようにこちらを使用しています。
なお、ここでは「echo」コマンドで、実行する予定のコマンドを表示するだけにしています。「echo」を外すと、実際にファイルのコピーや圧縮が行われます。
#! /bin/bash for filename in "$@" do backupname=$filename.`date +%Y-%m-%d` echo cp "$filename" "$backupname" echo gzip --force "$backupname" done
$ filebackup file1 file2 cp file1 file1.2018-12-26 gzip --force file1.2018-12-26 cp file2 file2.2018-12-26 gzip --force file2.2018-12-26 #(このようなコマンドが実行される予定)
このスクリプトでは、引数でファイル名を指定します。そこで、“指定されたファイルが存在したら〜”という条件を追加してみましょう。この場合、if文を使って、「if [ -f 変数 ]」のように指定することで確認できます。「[」の後ろや「]」の前の空白を入れ忘れないように注意してください。
if文については本連載の第26回、条件の書き方については第27回を参照してください。
なお、このような場合、for文のブロック中のif文は、行頭に空白やタブを入れて「インデント(字下げ)」した方が、処理のまとまりが分かりやすくなるでしょう。
#! /bin/bash for filename in "$@" do if [ -f "$filename" ]; then #ファイルが存在した場合のみ実行 backupname=$filename.`date +%Y-%m-%d` echo cp "$filename" "$backupname" echo gzip --force "$backupname" fi done
$ ls file? file1 file2 file3 #(file1、file2、file3がある) $ ./filebackup file1 file4 #引数として「file1」と「file4」を指定してみる cp file1 file1.2018-12-26 gzip --force file1.2018-12-26 #(file1とfile4を指定したが、file1だけが処理された(※)現時点では実行したいコマンドをechoで表示している)
ファイルがgzipで圧縮済みの場合は処理しない、つまり、バックアップ対象としないようにしてみましょう。判定はファイル名で行い、末尾が「.gz」かどうかをチェックします。
具体的には、“変数から「.」より前を取り除いた結果が「gz」ではない”と考えて、「${filename##*.} != gz」とします。「!=」は一致していないという意味の演算子です。
#! /bin/bash for filename in "$@" do if [ -f "$filename" ] && [ "${filename##*.}" != "gz" ]; then backupname=$filename.`date +%Y-%m-%d` echo cp "$filename" "$backupname" echo gzip --force "$backupname" fi done
現状のfilebackupスクリプトでは、名前に空白を含んだファイル、例えば「01 My Song.mp3」のようなファイルでは、「cp 01 My Song.mp3 01 My Song.mp3.2018-12-11」のようなコマンドが実行されてしまいます。この場合、cpコマンドに対して指定しているファイル名は、「01」と「My」と「Song.mp3」のような意味になっています。
これを防ぐためには「01\ My\ Song.mp3」のように空白をエスケープするか、「"01 My Song.mp3"」ファイル名全体を引用符(")で囲む必要があります。
単にcpコマンドを実行する場合は「cp "$filename" "$backupname"」とします。ここでは、echoコマンドでコマンドを表示しているので、「"〜"」だけだと引用符が表示から消えてしまうため、「"〜"」と「'〜'」を組み合わせて「"'$変数'"」としています。
#! /bin/bash for filename in "$@" do if [ -f "$filename" ] && [ "${filename##*.}" != "gz" ]; then backupname=$filename.`date +%Y-%m-%d` echo cp "'$filename'" "'$backupname'" echo gzip --force "'$backupname'" fi done
$ touch "01 My Song.mp3" #確認用に名前に空白が入ったファイルを作成 $ ./filebackup *.mp3 cp '01 My Song.mp3' '01 My Song.mp3.2018-12-11' gzip --force '01 My Song.mp3.2018-12-11' #(ファイル名に引用符がついた)
実行内容が確認できたら、「echo」を「eval」に置き換えます。evalは“式を評価する”という趣旨のコマンドで、「eval 文字列」で文字列をコマンドとして実行できます。
ここでは、echoコマンドで表示されていた内容が、そのままコマンドとして実行されることになります。
#! /bin/bash for filename in "$@" do if [ -f "$filename" ] && [ "${filename##*.}" != "gz" ]; then backupname=$filename.`date +%Y-%m-%d` eval cp "'$filename'" "'$backupname'" eval gzip --force "'$backupname'" fi done
$ ls * 01 My Song.mp3 file1 file2 #(3つのファイルがある) $ ./filebackup * #echoをevalに変更したスクリプトを実行 $ ls * 01 My Song.mp3 file1.2018-12-11.gz 01 My Song.mp3.2018-12-11.gz file2 file1 file2.2018-12-11.gz #(それぞれのファイルが日付付きの名前で圧縮された)
なお、本稿では、練習にechoコマンドでコマンド列を表示しながら段階を追って加工していることから、引用符の処理部分がやや複雑になっていますが、同じ処理は以下のように書くこともできます。cpの行とgzipの行はこちらの書き方の方が一般的です。
#! /bin/bash for filename in "$@" do if [ -f "$filename" ] && [ "${filename##*.}" != "gz" ]; then backupname=$filename.`date +%Y-%m-%d` cp "$filename" "$backupname" gzip --force "$backupname" fi done
また、スクリプトを作成せずに、コマンドラインでfor文を使ってファイルを順番に処理することも可能です。実行例は、LinuxコマンドTipsの第216回および第217回で紹介しています。
西村 めぐみ(にしむら めぐみ)
もともとはDOSユーザーで「DOS版UNIX-like tools」を愛用。ソフトハウスに勤務し生産管理のパッケージソフトウェアの開発およびサポート業務を担当、その後ライターになる。著書に『図解でわかるLinux』『らぶらぶLinuxシリーズ』『Accessではじめるデータベース超入門[改訂2版]』『macOSコマンド入門』など。地方自治体の在宅就業支援事業にてMicrosoft Officeの教材作成およびeラーニング指導を担当。会社などの“PCヘルパー”やピンポイント研修なども行っている。
Copyright © ITmedia, Inc. All Rights Reserved.