Linuxゲリラ戦記

私の人生には引数は無いのだろうか?

47.シェルスクリプトに引数をプレゼント

ナックス「プレゼント」

デビー君「?」

ナックス「もらうと大変嬉しいですよね?」

デビー君「うん。そりゃープレゼントをもらえば嬉しいよ?」

ナックス「私はデビー君に『知識』という名のプレゼントをあげつづけています。いつか、金銭的な物で返してくれると信じて」

デビー君「何を言ってるんだよ?僕だってナックスにお金では買えないものを与えているじゃないか!」

ナックス「え?」

デビー君「『この少年は大きくなったら、どんな凄いLinuxプログラマーになるんだろう?』という夢をね……!!」

ナックス「……けっ!!さぁ、今回は遂に前回の物をシェルスクリプトにしつつ、レベルアップさせていきます!!」

デビー君「なんだか舌打ちの音が聞こえたけど、きっと気のせい……」

ナックス「今回の目玉はシェルスクリプトにプレゼントをあげることです!!」

デビー君「プレゼント?」

ナックス「そうです!でも、まずは前回のコマンド」

curl -e http://www.my-ajax-site.com \
        'http://ajax.googleapis.com/ajax/services/search/images?q=%E5%A3%81%E7%B4%99&v=1.0&hl=ja&rsz=large&start=0&safe=off' \
| tr , \\n | grep "url" | sed -e 's/"url"://g' -e 's/"//g' > ahaha.txt
wget -i ahaha.txt

ナックス「を、nanoコマンドでもつかってテキストファイルに書き込みましょう。ファイルの名前はimageGet.shとでもしましょう」

いつも通りCUI環境を開く。
$ nano imageGet.sh

中身に、以下の様に書く

#!/bin/sh

curl -e http://www.my-ajax-site.com \
        'http://ajax.googleapis.com/ajax/services/search/images?q=%E5%A3%81%E7%B4%99&v=1.0&hl=ja&rsz=large&start=0&safe=off' \
| tr , \\n | grep "url" | sed -e 's/"url"://g' -e 's/"//g' > ahaha.txt
wget -i ahaha.txt
rm ahaha.txt

前回までのコマンドをシェルスクリプトにしよう

Ctrlキーを押しながらOキーを押して、Enterキーで保存
Ctrlキーを押しながらXキーを押してnanoコマンドを終了させる

ナックス「コマンドの最後に」

rm ahaha.txt

ナックス「を使ってahaha.txtを削除するようにしました。特にahaha.txtを取っておく必要は無いですからね」

デビー君「あれ?シェルスクリプトってこんな使い方もできるの?」

ナックス「え?」

デビー君「確か、今まで作ったシェルスクリプトでは while とか 変数 を使っていたから、そういう特殊なものを使った場合でしかシェルスクリプトを作っちゃいけないんだと思ってた」

ナックス「いえいえ。そんなことは無いです。シェルスクリプトは『たくさんのコマンドをまとめて書いておけば、スクリプト実行で一度にコマンドが実行できる』というのがメインで、むしろ while や 変数 はその補助的な物です」

ナックス「なので、例えば以下のように書いたシェルスクリプトもありです」

#!/bin/sh
ls

デビー君「え!lsって書かれただけのシェルスクリプトもありなの!?」

ナックス「そうです。暇な時に試してみてください」

ナックス「はい。というわけで、imageGet.shに実行権限をつけて」

$ chmod u+x imageGet.sh

ナックス「実行し、問題なく動くか確認してみてください」

$ ./imageGet.sh

デビー君「おー。画像が問題なくダウンロード出来ますなー」

ナックス「シェルスクリプト化がうまくいきましたら、今回の課題はプレゼント!今回はシェルスクリプトに『検索キーワード』をプレゼントします!」

デビー君「シェルスクリプトに検索キーワードをあげるの?」

ナックス「そうです。現在は『壁紙』というキーワードをURLエンコードした物を直に書いてしまっていますが、プレゼント形式にすることで、柔軟に検索キーワードを変更することが出来るようになります」

ナックス「プレゼント形式にした際のシェルスクリプトの実行方法はこんな感じ」

$ ./imageGet.sh "壁紙"

ナックス「になります。もちろん、"壁紙"の所を自分の好きな文字に変えることで、検索キーワードを変えることが出来ます」

デビー君「おー」

ナックス「それでは、先ほどのimageGet.shをnanoコマンドで編集していきましょう」

$ nano imageGet.sh

ナックス「実は、プレゼントというのは簡単に $1 というキーワードを使えば受け取ることが出来ます」

デビー君「ドルいち?」

ナックス「そうです。分かりにくいので、その $1 を、まずは keyword という変数に入れてみますか」

#!/bin/sh

keyword=$1
curl -e http://www.my-ajax-site.com \
        'http://ajax.googleapis.com/ajax/services/search/images?q=%E5%A3%81%E7%B4%99&v=1.0&hl=ja&rsz=large&start=0&safe=off' \
| tr , \\n | grep "url" | sed -e 's/"url"://g' -e 's/"//g' > ahaha.txt
wget -i ahaha.txt
rm ahaha.txt

ナックス「一番最初に」

keyword=$1

ナックス「という一文を追加しました。これにより、keywordという名前の変数にプレゼント(検索キーワード)が入りました」

ナックス「しかし、検索キーワードはURLエンコードしなければ使えないのでした。keywordをURLエンコードして、再度keywordに入れましょう」

#!/bin/sh

keyword=$1
keyword=`echo $keyword | nkf -wMQ | tr = %`
curl -e http://www.my-ajax-site.com \
        'http://ajax.googleapis.com/ajax/services/search/images?q=%E5%A3%81%E7%B4%99&v=1.0&hl=ja&rsz=large&start=0&safe=off' \
| tr , \\n | grep "url" | sed -e 's/"url"://g' -e 's/"//g' > ahaha.txt
wget -i ahaha.txt
rm ahaha.txt

ナックス「keyword=$1のすぐ下に」

keyword = `echo $keyword | nkf -wMQ | tr = %`

ナックス「という一文を入れました。これで、プレゼント(検索キーワード)がURLエンコードされたことになります。コマンドの結果を変数に入れる場合はバッククォート(`)で囲みます」

ナックス「最後はこのキーワードを実際にAPIのアドレスに入れましょう」

#!/bin/sh

keyword=$1
keyword=`echo $keyword | nkf -wMQ | tr = %`
curl -e http://www.my-ajax-site.com \
        'http://ajax.googleapis.com/ajax/services/search/images?q=${keyword}&v=1.0&hl=ja&rsz=large&start=0&safe=off' \
| tr , \\n | grep "url" | sed -e 's/"url"://g' -e 's/"//g' > ahaha.txt
wget -i ahaha.txt
rm ahaha.txt

ナックス「6行目の、URLエンコードされた『壁紙』という文字の代わりに${keyword}という文字を入れました」

デビー君「あれ?{}っていうのが新しくくっついてるね。これは何?」

ナックス「じつは{}無しでくっつけると」

'http://ajax.googleapis.com/ajax/services/search/images?q=$keyword&v=1.0&hl=ja&rsz=large&start=0&safe=off' \

ナックス「シェルスクリプトさんとしては」

$keyword&v=1.0&hl=ja&rsz=large&start=0&safe=offという名前の変数か?

ナックス「と勘違いをしてしまう……つまり、どこからどこまでが変数名か判断がつかなくなってしまうのです。それを防ぐために${keyword}として『変数名はここからここまでです』とシェルスクリプトさんに教えてあげているのです」

デビー君「なるほど」

ナックス「しかし、このままではうまく動きません」

デビー君「え?なんで?」

ナックス「アドレスに注目してみましょう」

'http://ajax.googleapis.com/ajax/services/search/images?q=${keyword}&v=1.0&hl=ja&rsz=large&start=0&safe=off' \

ナックス「アドレスの前後がシングルクォーテーション(')で囲まれてしまっています」

ナックス「文字の前後がシングルクォーテーションで囲まれている場合、中に何が書かれていようとも、シェルスクリプトは文字と認識します」

ナックス「つまり、${keyword}という変数も『変数じゃなくて、${keyword}という文字だな』と認識してしまうのです」

デビー君「へー。じゃあ、前後のシングルクォーテーションを取り除けばいいね」

http://ajax.googleapis.com/ajax/services/search/images?q=${keyword}&v=1.0&hl=ja&rsz=large&start=0&safe=off \

ナックス「はい。しかし、ただ取り除くだけでは駄目です」

デビー君「え?どうして?」

ナックス「イコール(=)の文字と、アンパサンド(&)の文字は、コマンド上では特殊な意味の文字です(例えば、イコールは、変数に値を代入するための文字ですね。アンパサンドはコマンドをバックグラウンドで実行する場合に使います。アンパサンドの特殊な使い方に関しては、後日解説する予定です)」

デビー君「え。つ、つまり……」

ナックス「はい。イコールとアンパサンドの前に、怒涛のエスケープ処理を施しましょう」

#!/bin/sh

keyword=$1
keyword=`echo $keyword | nkf -wMQ | tr = %`
curl -e http://www.my-ajax-site.com \
        http://ajax.googleapis.com/ajax/services/search/images?q\=${keyword}\&v\=1.0\&hl\=ja\&rsz\=large\&start\=0\&safe\=off \
| tr , \\n | grep "url" | sed -e 's/"url"://g' -e 's/"//g' > ahaha.txt
wget -i ahaha.txt
rm ahaha.txt

シェルスクリプトをバージョンアップしよう

ナックス「こいつでオッケーなはずです」

デビー君「け、結構大変だったね。だから最初はシングルクォーテーション(')で囲んでいたのか……」

ナックス「というわけで、こいつを保存しましょう」

Ctrlキーを押しながらOキーを押して、Enterキーで保存
Ctrlキーを押しながらXキーでnanoを終了

ナックス「さて、実際に犬、というキーワードで試してみますか」

$ ./imageGet.sh "犬"

デビー君「どうやら成功したみたい」

犬の画像がダウンロードできましたか?

ナックス「というわけで今回は検索キーワードを柔軟に変更出来るようにバージョンアップしてみました」

デビー君「そうだね。これなら」

$ ./imageGet.sh 犬 壁紙

デビー君「で、犬関係の壁紙、とか、欲しいものをすぐに検索キーワードで指定できて便利だね!」

ナックス「のおおおおおおおおおおおおおおおおおおおおおおおおおおおおおお!!」

デビー君「!!?え?何!?僕、なんか悪いことした?」

ナックス「そういう場合、正しくは」

$ ./imageGet.sh "犬 壁紙"

ナックス「としてください」

デビー君「あ。プレゼントはダブルクォーテーション(")で囲まないと駄目なの?」

ナックス「いえ。そういうわけじゃないんです。えーっと、一つ一つ説明しますと、実はシェルスクリプトに渡すプレゼントは一つだけじゃなくても良いのです。つまり、2つでも、3つでも、100個でも良いのです」

デビー君「え!そうなんだ」

ナックス「そうです。そして、プレゼントの受け取り方も、一個の時と同じく簡単です。2つ目のプレゼントを受けとるときは」

$2

ナックス「3つ目のプレゼントを受けとるときは」

$3

ナックス「100個目のプレゼントを受けとるときは」

$100

ナックス「とすれば良いのです」

デビー君「おお。単純明快分かりやすい」

ナックス「そうです。そして、先ほどのデビー君のやり方」

$ ./imageGet.sh 犬 壁紙

ナックス「は、実はimageGet.shというシェルスクリプトに2個のプレゼントを渡す方法なんです。一つ目のプレゼントは『犬』。そして、二つ目のプレゼントは『壁紙』。でも、今回は一つのプレゼントだけを受けとることを前提としています。つまり」

$ ./imageGet.sh 犬 壁紙

ナックス「とした場合、一つ目の『犬』というプレゼントしか受け取ってくれず、意図しない動作となってしまいます」

ナックス「ですが、ダブルクォーテーションで囲むことで」

$ ./imageGet.sh "犬 壁紙"

ナックス「一つ分のプレゼントを明示的に指定するとができます」

デビー君「なるほど」

ナックス「逆に言えば、検索キーワードが一つの場合……、つまり、半角スペースが入らない場合は、ダブルクォーテーションは必要ありません」

$ ./imageGet.sh 子猫

ナックス「上記の様にして、うまく子猫関係の画像がダウンロードできるはずです」

デビー君「へー」

ナックス「さて、今回出てきた『プレゼント』。正式名称は引数と言います」

デビー君「『ひきすう』?」

ナックス「そうです。今回は引数に検索キーワードを指定できるようにしたのです!一個目のプレゼントのことを第一引数。二個目のプレゼントのことを第二引数、みたいな感じで言います!」

デビー君「『ひきすう』……なぜそんな訳の分からない名前なんだ……」

ナックス「そんな事は知らん!!さて次回。『GoogleAPIの限界を探りつつ、100個の画像を一度にダウンロードできるようにする』です(48.シェルスクリプトにif文、exit。及び、wget)」

補足

怒涛のエスケープ処理の代わりに、シングルクォートで無くてダブルクォートで囲むと上手く動きましたが?

ごめんなさい。その通りです。

シングルクォート(')で文字列を囲んだ場合、中の文字に変数があっても変数名をそのまま文字として認識してしまいます。

ダブルクォート(")で文字列を囲んだ場合、文字の中に変数があった場合は、きちんと変数を変数の中に入れてあるモノに入れ替えて、文字とします。

よって、上記の解説にあるコードよりは下記の方がより好ましいコードです。

#!/bin/sh

keyword=$1
keyword=`echo $keyword | nkf -wMQ | tr = %`
curl -e http://www.my-ajax-site.com \
        "http://ajax.googleapis.com/ajax/services/search/images?q=${keyword}&v=1.0&hl=ja&rsz=large&start=0&safe=off" \
| tr , \\n | grep "url" | sed -e 's/"url"://g' -e 's/"//g' > ahaha.txt
wget -i ahaha.txt
rm ahaha.txt

これ以降解説上に登場するコードも、怒涛のエスケープ処理のやり方で書いてしまっておりますが、ダブルクォーテーションの書き方で書いて構いません。(ダブルクォーテーションで書いた方法の方がより好ましいです)。

本当はきちんと解説上のコードを全て書き直すべきなのですが、ワタクシは今、社会の荒波に揉みに揉まれて時間がありません。ごめんなさい。

また、他にもコードや解説において間違いがあるかもしれません。もし見つけた際はメールでご指摘いただけると嬉しいです。

使用しているGoogle APIが古く、利用できないようになっているのですが。

そうですね。そこで、代わりとなるアドレスを用意しました。解説で使用している

http://ajax.googleapis.com/ajax/services/search/images

の代わりに

http://www.garunimo.com/program/linux/search/images.php

を使用してください。代わりに用意したこちらのアドレスですが、実際の検索機能は無いですが、「rsz=largeを指定すると8個のデータを返す。指定しない場合は4個のデータを返す」「実際に画像がダウンロードできるアドレスを返す」の2つの機能を備えています。ちなみに、ダウンロードできる画像は、あの有名なかわいいフリー素材集 いらすとやの画像を使用しています。

当サイト側で用意したアドレスで、解説上のコードを記述した例は以下となります。

#!/bin/sh

keyword=$1
keyword=`echo $keyword | nkf -wMQ | tr = %`
curl -e http://www.my-ajax-site.com \
        http://www.garunimo.com/program/linux/search/images.php?q\=${keyword}\&v\=1.0\&hl\=ja\&rsz\=large\&start\=0\&safe\=off \
| tr , \\n | grep "url" | sed -e 's/"url"://g' -e 's/"//g' > ahaha.txt
wget -i ahaha.txt
rm ahaha.txt
このエントリーをはてなブックマークに追加