2015年7月8日水曜日

Posted by mattintosh | File under : , , , , ,
最初の方に書いておくべきでしたが変数などのお話です。

「変数とはなにか?」についてはここでは詳しくお話しません(ウェブ上にたくさん情報があるので検索してみてください)。

変数の型

C 言語などでは変数には「型」を指定する必要がありますが、bash の変数は何でも入ります(nul は入らなかったと思いますが)。

そんな何でもアリな bash の変数ですが、declare を使うことで整数値として宣言することができます。 declare -i で宣言しているものは英字を入れても 0 になります(※記号などを代入しようとするとエラーになります)。

変数展開時の {} って必要?

これまでのスクリプトではすべて ${PATH} のように {}(ブレース⁄brace)を記述してきましたが、$PATH と書いても問題はありません。私はスクリプトではできるだ書くようにしていますが、対話モードでは必要なときだけつけています。

ブレースが必要になるパターンをいくつかお話します。

変数名の区切りとして

{} が必要なとき」というのは下の実行結果を見てみてください。 変数名にブレースがない場合は「$ から変数名として使える文字が終わるところまで」が変数名になります。3行目と5行目を見比べてみるとわかりますが、ブレースを使うことで「どこからどこまでが変数名なのか?」を指定することができます。

7行目のような書き方でも展開はできますがあまりこういった書き方はしません(私はですが)。

8行目ではブレースなしでも $BASH が正しく展開されていますが、$BASH.VERSION という変数は存在しない(というか . があるので宣言できない)ので $BASH という変数と .VERSION という文字列として認識されます。

よくある間違いは変数と _ を使って文字を連結する場合です。なぜ _ の方だけ $BASH 部分が展開されていないかはもうわかりますね?

配列の展開のために

BASH_VERSINFO は bash のバージョン情報が入った配列変数です。 BASH_VERSINFO をブレースありとなしで出力してみます。 ブレースなしの方は $BASH_VERSINFO という変数と [〈インデックス〉] という文字列として認識されます。 今度は for を使ってインデックスを変数から展開します。 先程と同じくブレースなしの方は正しく展開されていません。また、ブレース内の [〈変数〉]$ を付けなくても展開されていますが、ブレースなしの方はそちらも展開されていません。 配列変数を展開するにはブレースが必要になります(※0番目の要素は除く)。$BASH_VERSINFO[0] と書いたものは ${BASH_VERSINFO}[0] と書いたときと同じ認識になってるんですね。

置換展開のため

bash を含め、いくつかのシェルではブレースを使った変数展開の際に、特殊な展開を行うことができます。 シェルのコマンドには Ruby などにある拡張子だけを取り出す extname() のようなものがありません(知らないだけかもしれませんが)。ですが、上記のような置換展開を使うことで cut や sed のようなコマンドに頼らず拡張子だけを取り出すことができます。
ブレースが必要になるパターンというのはおわかりいただけたでしょうか?私は「後で書くくらいなら最初から書いておく」タイプなので書いてますが、先に述べたようにブレースなしでも動くものは動きます。

変数に関することはまだまだたくさんあるのですが、長くなってしまったので今回はこの辺で終わりにしたいと思います。

最後にお遊びスクリプトとして画像ファイルを一括で JPEG に変換するスクリプトを紹介します。convert は ImageMagick に含まれている画像変換プログラムです。

case を併用することで拡張子がついているかどうかをより正確に判断できるようになります。

休みの日にだらだらと書いてしまったので説明の順序がごちゃごちゃになってしまいました。ごめんなさい。

ではまたそのうちお会いしましょう(・∀・)ノシ

2015年6月23日火曜日

Posted by mattintosh | File under : ,
今まではスクリプトの先頭に #!/bin/bash と書いていました。 カレントシェルが bash の場合、実行権限を付与してスクリプトを直接実行すると正しい結果が得られます。 先ほどのスクリプトを bash 以外のシェルで実行してみます(ここでは zsh を使用していますが ksh などでもかまいません)。${BASH_VERSION} は実行するシェルが bash の場合に設定されている変数であるため、それ以外のシェルでは何も表示されません。
今度はシェバンのシェルのパスを書き換えてみます。 ファイル名のみで実行してみると何も表示されないことがわかると思います。 今度は同じファイルを bash で実行してみます。これはバージョンが表示されます。 実行方法やシャバングなどの関係を以下にまとめてみました。
シェル 実行方法 実行権限 シェバン 実行シェル
bash      ./gistfile1.sh なし bash
     ./gistfile1.sh #!/bin/bash /bin/bash
     ./gistfile1.sh #!/bin/sh /bin/sh
bash ./gistfile1.sh 不要 なし bash
bash ./gistfile1.sh 不要 #!/bin/bash bash
bash ./gistfile1.sh 不要 #!/bin/sh bash
sh   ./gistfile1.sh 不要 なし sh
sh   ./gistfile1.sh 不要 #!/bin/bash sh
sh   ./gistfile1.sh 不要 #!/bin/sh sh
sh      ./gistfile1.sh なし sh
     ./gistfile1.sh #!/bin/bash /bin/bash
     ./gistfile1.sh #!/bin/sh /bin/sh
bash foo.sh のように実行した場合、シェバンによるシェルの指定は無視されます。例えば、ソースファイルに含まれる configure などのスクリプトを実行するときに #!/bin/sh が記述されたものに対して zsh configure として実行すると正しく動作しないかもしれません。
Posted by mattintosh | File under : , , , , ,
前回、typedeclare というコマンドを使いました。これらは bash に含まれる内蔵(組み込み)コマンドです。「内蔵」というからには「外部」もあるのですが、lscat など、その他多くのコマンドが外部コマンドです。

内蔵コマンドは enable コマンドや help コマンド、man bashman bash-builtins で確認することができます。 前回のコマンド検索では type を使いましたが、実際にコマンド検索でよく使われるのは which です。なぜ which ではなく type を使ったかというのは以下のスクリプトを実行してみてください。 このコマンドの結果は以下のようになります。 どちらも unset PATH した後にエラーが出ていますが、エラーの内容が異なります。type の方は「ls コマンドが見つからない」と出ていますが、which の方は「そのようなファイルやディレクトリはない」と出ています。これは which が外部コマンドなので unset PATH すると、コマンド名だけでは呼び出しができず、which を実行ができないからです。前回、type を使ったのはそのためです(which にすらパスが通ってない環境ではあまり意味がありませんが)。

type は引数に指定されたコマンドがシェルのエイリアスや関数であればその内容を表示します(これはオプションによって変わります)が、which は外部コマンドのみを探すという違いもあります。

内蔵コマンドと外部コマンドの特徴

内蔵コマンドと外部コマンドの特徴を以下に挙げます。

内蔵コマンドの特徴

  • 外部コマンドより速い
  • PATH に依存しない
  • bash 独自のコマンドがあるため他のシェルで使えない可能性がある(例:ksh で declare は使えない)
  • バージョンによって実装されていないコマンドやオプションがある(例:readarray などは bash 3.2 では使えない)

外部コマンドの特徴

  • 内蔵コマンドより遅い
  • PATH に依存する(※ファイル名のみで呼び出す場合)
  • バージョンやシェルの種類が異なっても同じ動作が期待できる(オプションの種類など)
内蔵コマンドと外部コマンドの両方にメリット・デメリットがあります。プロジェクトなどのルールで実行シェルが bash と決まっているのであれば内蔵コマンドでもいいと思います(ただ、bash に限定してスクリプトを書くことはほとんどないと思います…)。

速度の違い

以下は内蔵コマンドと外部コマンドの速度を比較するためのスクリプトです。 内蔵コマンドの方は0秒未満で終了していますが、外部コマンドの方は16秒近くかかりました。

オプションの違い

Lesson 2 で echo を作ったスクリプトの話をしましたが、「意図しない結果が出力されている部分がある」と話したのを覚えているでしょうか? すでにおわかりだと思いますが、スクリプト内にコメントとして書いていたものは外部コマンドの方の echo の書式です。内蔵の echo はロングオプション(--help--version)を受け付けません。help echoman echo で比較してみてください。
# 内蔵 echo の書式
echo [-neE] [arg ...]

# 外部 echo の書式
echo [SHORT-OPTION]... [STRING]...
echo LONG-OPTION

enable も使ったサンプルを置いておきますのでご自由にお試しください。 which の他に whereis というコマンドもありますのでそちらもチェックしてみてください。

2015年6月22日月曜日

Posted by mattintosh | File under : , , , , ,
今回はコマンドがどのように呼び出されているかというお話です。

皆さんコマンドを実行するときはコマンド名だけを入力していると思います。とりあえず以下のようなスクリプトを作成して実行してみてください。 実行結果は以下のようになります。2回目の bash --version./gistfile1.sh: line 6: bash: No such file or directory というエラーになっているのがわかります。 そもそも bash というコマンドはどこにあるのでしょうか?ターミナルで確認してみましょう。(type は bash のビルトインコマンドです) bash は /bin/bash にあるようです。

では、普段よく使う lscat も同じように探してみましょう。 これらは /bin/ls/bin/cat にあるようです。(※環境によっては /usr/bin 以下に格納されていることもあります)

/bin という場所にあるのになぜコマンド名だけで呼び出せるのか?それは PATH 変数のおかげです。PATH 変数というのはコマンドを探すためのもので、:(コロン)で区切られたリストです。

実際にどのようなものか見てみましょう。変数は echodeclare(bash builtin) などで確認することができます。 少し整形します。 フルパスではないコマンドが指定された場合、bash は PATH 変数のリストの最初から順番にコマンドを探していきます(上の場合だと /usr/local/bin)。この仕組みのおかげでいちいちフルパスでコマンドを指定しなくてもコマンドを実行することができるようになっています。逆に言えば PATH 変数に含まれていない場所にあるコマンドはコマンド名だけで呼び出すことができません。

さて、上の PATH 変数の内容ですが、あなたの環境で実行した結果は上のものと同じだったでしょうか?恐らく違う結果だったのではないかと思います。なぜ違うのかというと、環境変数の話も絡んできますので今回は「コマンド名だけで呼び出された外部コマンドは PATH 変数のリストの左から順番に検索される」とだけ覚えておいてください。
Posted by mattintosh | File under : , ,
特にお題がない ヽ(´ー`)ノ


まずはスクリプトを書いてみましょう。

テキストエディタで以下のようなスクリプトを作成してみてください。ファイル名は「sample.txt」とし、ファイルの保存場所はホームディレクトリとします。
スクリプトを保存したら、ターミナル上で chmod コマンドで実行権限を付与します。
vi を使っている場合は :!chmod +x % で vi を終了せずに実行権限を付与できます。Komodo Edit を使っている場合は Ctrl + R でコマンド実行画面を呼び出せます。%F は現在のファイルのフルパスに置き換えられます。
statls -l でファイルを確認してみましょう。実行権限が付与されていることを確認してください。
~/sample.sh を実行します。 うまくメッセージが表示されたでしょうか?

では先程のファイルにいくつか記述を追加してみます。 変更点は以下の差分を確認してください。先頭に + がついている行が追加された行です。いずれこの差分の見方も紹介します。 実行権限は既に付与されているのでそのまま先程と同じように実行します。先程の出力よりかなり多くなっていますが、ところどころに出力されている >>> 部分に注目してみてください。 所々にスクリプトに書いてあるコマンドが出力されているのがわかるでしょうか?これは bash の xtrace オプション(set -x)によるものです。デバッグなどに使ったりするオプションですが、練習用におおすすめです。無効にする場合は set +x のようにオプションの符号を反転させます。

xtrace オプションを有効にするには、上記のスクリプトのように有効にしたいところに set -x または set -o xtrace を記述します。また、シェバングに #!/bin/bash -x を記述しても有効にすることができます。シェバングに記述する方がシンプルですが、無効にする場合にいちいち削除しなくてはいけないので set -x がおすすめです。

xtrace の有効・無効確認用のスクリプトです。 実行結果は下記のようになります。
スクリプトを書いて実行するところまではできたでしょうか?なお、xtrace はスクリプト内だけではなくターミナル上でも使うことができます。似たような機能で set -v もありますが、xtrace の方がコマンドの出力や引数がわかりやすくなっています。

実は今回作成したスクリプトには意図しない結果が出力されている部分があるのですがそれはまた今度お話します。
Posted by mattintosh | File under :
まず最初はシェルから始めようと思います。Blogger に慣れていないので最初のうちは書き方がころころ変わるかもしれませんがご了承ください。

スクリプトからシェルに慣れていく

シェルに慣れるためにはコマンドをひたすら実行するというのもありますが、「スクリプトを書く」というのがおすすめです。以下におすすめポイントを挙げます。
  • ゆっくり書ける
  • 再実行が簡単
  • 記録として残る(必要なくなったりすればコメントアウトするだけで良い)
  • パイプや演算子を使った複数コマンドの連結がしやすい
  • for などのループ文や if などの複数行文が書きやすい
  • 関数が書きやすい
  • エディタのシンタックスハイライトを利用できる
例えば、もしあなたが英語に慣れていない場合、(人によるかもしれませんが)いきなり話すより紙に書いてみた方が文が組み立てやすいですよね?シェルも同じようにスクリプトを書くことで文が組み立てやすく、理解しやすくもなります。

シェルに関しては Wikipedia などを見ていただければわかると思うのですが、簡単に言ってしまうとひとつの「言語」です。「言語」ですので我々が使っている日本語や英語と同じように規則があります。それらきちんと覚えることで日常で会話するのと同じようにターミナルでコマンドを実行できるようになるはずです。 LPIC の勉強を始めた方はまず、ターミナル(「端末」、「コンソール」と言ったりもしますが、ここでは「ターミナル」で統一します)で lscat などのコマンドを叩いて練習すると思います。そこで何か気づいたことはあるでしょうか?

英語では命令形の場合、動詞が一番最初にきますよね?コマンドも「命令」ですので英語と同じようにコマンドが一番最初にきます(※必ずしも最初にくるわけではありません)。
上記のコマンドの場合、コマンドは cat引数sample.txt です。もし、cat を省略したり、catsample.txt を引数を入れ替えてしまうと sample.txt がコマンド(もしくはスクリプト)。最悪の場合、sample.txt に書かれているスクリプトが実行されてしまう可能性もありますので順番はとても重要です。

このような規則の他にもシェルごとの方言なんかもあったりするので追々話していきたいと思います。

シェルの種類

シェルにはいくつか種類があります。
  • sh
  • bash
  • csh
  • ksh
  • zsh
上記以外にもたくさんの種類があります。最も多く使われているのは bash だと思います(脆弱性で話題になりましたが)。LPIC Level 1 の試験ではそれ以外のシェルは出てこない(ような気がする)なので、ここでは特に記載がない限りは bash の説明だと思ってください。bash のバージョンについても特に記載がなければ 4.3 と思ってください(「バージョンによって違いがある」ということはとりあえず覚えておいてください)。

エディタとフォントの準備

スクリプトを書くときにエディタとフォントはとても重要なものです。まずはそれぞれを選ぶときのポイントを挙げます。以下は私が個人的に重要視しているものであり、必ずしも要件を満たしているものが扱いやすいというわけではありません。

エディタ

  • シンタックスハイライトに対応している
  • 空白スペース、タブ、改行の表示に対応している
  • タブを空白に変換でき、インデントのサイズを指定できる
  • コマンド実行に対応している
  • UTF-8 に対応している(文字エンコーディングを変更することができる)
  • 改行コード LF に対応している(改行コードを変更することができる)
私は普段、ターミナルアプリケーション上で vim を使用していますが、GUI アプリケーションで作業するときは Komodo Edit を使用しています。Komodo Edit は Windows/Mac/Linux で使用することができます(ただし、Linux 版は手動でインストールする必要があります)。 Windows の場合は gvim や cygwin + vim などを使う手もあります。

フォント

  • 0O の見分けがつくもの
  • 1lI の見分けがつくもの
  • ij の見分けがつくもの
フォントのおすすめは DejaVu Sans MonoSource Code Pro です。フォントは色々な Web サイトからダウンロードできますが、おすすめをひとつご紹介しておきます。
なお、投稿時点ではこのブログの等幅フォントは Cousine を使用しています。

エディタとフォントのサンプル

Komodo Edit と Cousine というフォントの組み合わせたときのサンプルです。シンタックスハイライトはコードが正しくかけているかどうかの確認ができます。まだ慣れていないうちはクオートの閉じ忘れなどもよくあることですが、シンタックスハイライトによってそういったミスを見つけることができます。



ターミナルで emacs や vim を使う場合は分割表示可能なものをおすすめします。画像は Terminator というアプリケーションのものです。アプリケーションが分割表示に対応していなければ tmux の導入もおすすめです。



Linux ではエディタやフォント、ターミナルはたくさんありますので色々試してみて自分に合うものを探してみてください。
Posted by mattintosh | File under : , ,
ここに投稿している各種スクリプトは GitHub 上の Gist から呼び出しています。これらのスクリプトは curlwget などのコマンドを使ってシェルに直接渡すことが出来ます。

スクリプトの URL は埋め込みウィンドウの右下にある「view raw」から取得できます。

投稿上のスクリプトの実行方法

以下のスクリプトの URL を取得してください。 プロセス置換が使用可能な環境であれば以下のようにすることでシェルがスクリプトを直接読み込みます。 プロセス置換が使えないシェルの場合はヒアドキュメントとコマンド置換を使用してください。 curlwget からパイプでシェルへ渡してもかまいません。(この方法は前のコマンドを呼び出して書き換える場合に少し面倒です)
xclip が使える環境であればスクリプト全体をクリップボードにコピーして xclip を経由して実行させることもできます。(スクリプトの選択範囲や現在のクリップボードの内容に注意してください) curlwget に指定する URL をクリップボードから呼び出すようにしておけばクリップボードの内容を更新するだけになり、コマンドの書き換えが必要がなくなります。
下記のような結果が出力されればOKです。

スクリプトのダウンロード方法

各投稿のスクリプトは Gist のページで取得できる URL から Git を使って一括ダウンロードすることができます。
拡張子が「sh-session」のものはシェルのセッションを再現したものであるため、そのままでは実行できませんが、ブラウザ上でプロンプトを除いた部分をコピーすれば引用できるものもあります。