3.3 レッスン 1
Certificate: |
Linux Essentials |
---|---|
Version: |
1.6 |
Topic: |
3 コマンドラインの力 |
Objective: |
3.3 コマンドをスクリプトにする |
Lesson: |
1 of 2 |
はじめに
ここまではシェルからコマンドを実行することを学んできましたが、ファイルにコマンドを書いて実行可能なファイルとすることもできます。 ファイルが実行されると、それらのコマンドが次々と実行されます。このような実行可能ファイルを スクリプト と呼び、Linuxシステム管理者にとって極めて重要なツールです。Bashはシェルであるだけでなく、一種のプログラミング言語であると考えることができます。
文字を表示する
ここまでのレッスンで見たことがあるコマンドから見ていきましょう: echo
は引数を標準出力に表示します。
$ echo "Hello World!" Hello World!
次に、このコマンドを new_script
という新しいファイルにリダイレクトします。
$ echo 'echo "Hello World!"' > new_script $ cat new_script echo "Hello World!"
ファイル new_script
は、前の例と同じコマンドを内容としています。
スクリプトを実行可能にする
このスクリプトを、望むように実行可能とするために必要な手順を示しましょう。他のコマンドの名前を入力するのと同様に、スクリプトの名前をタイプすればよいと考えるかもしれません:
$ new_script /bin/bash: new_script: command not found
現在のディレクトリに new_script
が存在するはずです。エラーメッセージは、 ファイル が存在しないではなく、 コマンド が存在しないとなっていることがポイントです。ここで、Linuxがコマンドと実行可能ファイルを扱う方法を述べておきましょう。
コマンドと PATH
例えば、シェルに ls
コマンドを入力すると、ファイルシステムに存在する ls
というファイルを実行します。which
を使って調べてみましょう:
$ which ls /bin/ls
ディレクトリの内容を見たいときに毎回 ls
の絶対パスを入力するのはすぐに面倒になります。そのため、Bashは 環境変数 の中に、実行するコマンドがあると思われるすべてのディレクトリを納めています。この変数の内容は、echo
を使って見ることができます。
$ echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
シェルは、それぞれがコロン(:
)で区切られたディレクトリで、コマンドを見つけようとします。この中に /bin
はありますが、現在のディレクトリは存在しないことがポイントです。シェルはこれらのディレクトリそれぞれから new_script
を探しますがどこにも無いので、例に示したようなエラーとなるのです。
これを解決するには、3つの方法があります: new_script
を PATH
ディレクトリの1つに移動するか、現在のディレクトリを PATH
に追加するか、あるいは、スクリプトの呼び出し方法を変更します。最後の方法が最も簡単で、ドットスラッシュ(./
)を使用して、スクリプトを呼び出すときに 現在のディレクトリ を指定するのです。
$ ./new_script /bin/bash: ./new_script: Permission denied
エラーメッセージが変わり、ある程度の進展があったことが分かります。
実行パーミッション
この場合には、まず ls -l
を使ってファイルを調べます:
$ ls -l new_script -rw-rw-r-- 1 user user 20 Apr 30 12:12 new_script
このファイルのパーミッションが、デフォルトの 664
に設定されていることがわかります。このファイルには、まだ 実行パーミッション がセットされていません。
$ chmod +x new_script $ ls -l new_script -rwxrwxr-x 1 user user 20 Apr 30 12:12 new_script
このコマンドは、すべての ユーザーに実行許可を与えます。このパーミッションにはセキュリティ上のリスクがありますが、今のところは許容可能なレベルです。
$ ./new_script Hello World!
やった! スクリプトを実行できました。
インタプリタの指定
テキストをファイルに入力し実行可能と設定すれば、実行できることを見てきました。new_script
は通常のテキストファイルですが、Bashで解釈させることができました。しかし、スクリプトがPerlまたはPythonで記述されている場合はどうでしょうか?
スクリプトの最初の行に、使用するインタープリタの種類を指定することは非常に良い習慣です。この行は一般的に シェバング(shebang)行(訳注: シバンあるいはシェバンという発音が一般的です)、あるいは単に バング行(bang line)と呼ばれ、そのファイルを実行する方法をシステムに示します。今はBashを学習しているので、再度 which
を使用して、Bashの実行可能ファイルへの絶対パスを指定します:
$ which bash /bin/bash
シェバンはハッシュ記号( #
)と感嘆符( !
)で始まり、その後に上記の絶対パスを書きます。テキストエディターで new_script
を開き、シェバンを挿入しましょう。またこの機会に、スクリプトに コメント を挿入してみましょう。コメントはインタープリタでは無視されますが、スクリプトを理解したい他のユーザーのために書くものです。
#!/bin/bash # This is our first comment. It is also good practice to document all scripts. # 訳注: コメントを日本語で書くこともできますが、システムの言語設定によって # 表示・編集できないことがあるので、英語で記入することが好まれます echo "Hello World!"
ファイル名にも変更を加えましょう: このファイルを new_script.sh
として保存します。ファイルのサフィックス ".sh" は、ファイルの実行に一切関係ありません。Pythonスクリプトを .py
サフィックスで識別するのと同様に、bashスクリプトには .sh
または .bash
を付けるのが慣例です。
代表的なテキストエディタ
Linuxユーザーは、グラフィカルなテキストエディターが利用できない環境で作業する必要があることがよくあります。そのため、コマンドラインでのテキストファイル編集について、少なくともある程度慣れておくことを強く推奨します。最も一般的なテキストエディターは、vi
と nano
の2つです。
vi
vi
は由緒あるテキストエディタで、現存するほとんどすべてのLinuxシステムにデフォルトでインストールされています。vi
は、そのインターフェイスを維持しつついくつかの機能を追加した、vim
すなわち vi IMproved と呼ばれるクローンを生み出しました。 初学者にとって、vi
を使って作業するのは気が遠くなるほどですが、このエディタは人気があり、その多くの機能を学ぶユーザに愛されています。
vi
とメモ帳などのアプリケーションの最も重要な相違点は、vi
には3つのモードがあることです。起動時にはキー h
、 j
、 k
、 l
はナビゲーション(移動)に使われて、入力されることはありません。この ナビゲーションモード で、 i
を押すと 挿入モード になります。このモードでは、通常通りに文字の入力が行えます。挿入モード を抜けるには、 ESC
を押すと ナビゲーションモード に戻ります。ナビゲーションモード で、 :
を押すと コマンドモード に入り、保存や削除、終了、オプションの変更といった操作を行えます。
vi
には学習曲線があり、さまざまなモードを使うことで、経験豊富なユーザーほど他のエディタよりも効率的に作業が行えます。
nano
nano
は新しいツールで、vi
よりもシンプルで使いやすく作られています。nano
にはモードがありません。起動するとすぐに入力を開始することができ、Ctrl
を使って画面下部に表示されているツールにアクセスすることができます。
[ Welcome to nano. For basic help, type Ctrl+G. ] ^G ヘルプ ^O 書き込み ^W 検索 ^K 切り取り ^J 均等割付 ^C カーソル位置 ^X 終了 ^R 読み込み ^\ 置換 ^U 貼り付け ^T スペル確認^_ 行を指定
テキストエディタは個人の好みの問題であり、このレッスンではどれを使っても構いません。しかし、1つ以上のテキストエディタに慣れ親しんで快適に使いこなせるようになっておくと、報われることが多いでしょう。
変数
変数はプログラミング言語の重要なパーツであり、Bashも例外ではありません。ターミナルから新しいセッションを開始すると、シェルはすでにいくつかの変数を設定しています。PATH
変数はこの例の1つです。これらがシェル環境の特性を定義するので、環境変数(environment variables)と呼びます。環境変数は変更や追加できますが、ここではスクリプトにおける変数設定に焦点を当てましょう。
スクリプトを次のように変更します:
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username=Carol echo "Hello $username!"
この例では、username
という 変数 を作成し、Carol
という 値 を代入しています。変数名、等号、代入する値の間に、空白を置かないことに注意しましょう。
次の行では、echo
コマンドで変数を使用していますが、変数名の前にドル記号($
)を置きます。 このドル記号は重要で、username
を普通の単語ではなく変数名として扱うことをシェルに指示しています。コマンドに $username
を与えると 置換 が行われて、変数名 が変数に割り当てた 値 に置き換えられます。
新しいスクリプトを実行して、出力を見てみましょう:
$ ./new_script.sh Hello Carol!
-
変数名には、英数字とアンダースコア(
_
)のみが使用でき、大文字と小文字は区別されます。Username
とusername
は、別の変数として扱われます。(訳注: 変数名に日本語は使えません)。 -
変数の置換は、
{ }
で囲んで${username}
と書くこともできます。 -
Bashの変数に型はなく、すべてが文字列として扱われます。そのため、Bashでの数値演算は、C/C++などの他のプログラミング言語に比べると面倒です。
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username=Carol x=2 y=4 z=$x+$y echo "Hello $username!" echo "$x + $y" echo "$z"
$ ./new_script.sh Hello Carol! 2 + 4 2+4
変数でクオートを使う
変数 username
の値を次のように変更してみましょう:
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username=Carol Smith echo "Hello $username!"
このスクリプトを実行すると、次のようなエラーになります:
$ ./new_script.sh ./new_script.sh: line 5: Smith: command not found Hello !
Bashはインタープリタであるため、スクリプトを1行ずつ 解釈 することを覚えておきましょう。username=Carol
は、変数 username
に値 Carol
を割り当てるものと正しく解釈されます。しかし、その次のスペースをその割り当ての終わりを示すものと解釈し、Smith
を次のコマンドの名前として解釈します。スペースと名前 Smith
を変数の新しい値に含めるためには、名前を二重引用符( "
)で囲みます。
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username="Carol Smith" echo "Hello $username!"
$ ./new_script.sh Hello Carol Smith!
Bashでは、シングルクオート( '
)とダブルクオート( "
)の扱いが異なるので注意が必要です。ダブルクオートは引用内での置換が解釈されるため “弱い” 引用符です。対して、シングルクオートは置換が行われないので、 “強い” 引用符です。例を見てみましょう:
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username="Carol Smith" echo "Hello $username!" echo 'Hello $username!'
$ ./new_script.sh Hello Carol Smith! Hello $username!
2番目の echo
コマンドでは、$username
から Carol Smith
への置換が解釈されず、入力した通りの文字列が表示されます。
引数
Linuxユーティリティで引数を使う方法は既に学習しました。例えば、rm testfile
では、rm
が実行可能なコマンドで、testfile
が引数です。スクリプトの実行時に引数を渡して、その振る舞いを変更する事ができます。簡単に実装することができます。
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username=$1 echo "Hello $username!"
スクリプト内部で username
に値を直接割り当てるのではなく、新しい変数 $1
の値を割り当てます。これは、最初の引数 の値を意味しています。
$ ./new_script.sh Carol Hello Carol!
最初から9個目までの引数は、この方法で扱えます。9個を超える引数も扱えるのですが、このレッスンの範囲を超えてしまいます。2つの引数を扱う例を見てみましょう:
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username1=$1 username2=$2 echo "Hello $username1 and $username2!"
$ ./new_script.sh Carol Dave Hello Carol and Dave!
引数を使うときに大切なことがあります: 上の例では、2つの引数 Carol
と Dave
が、それぞれ $1
と $2
に割り当てられています。例えば、2番目の引数が無かったとしても、シェルはエラーとなりません。$2
に、単に null が割り当てられるだけです。
$ ./new_script.sh Carol Hello Carol and !
この場合、スクリプトに、条件 に応じて表示する 出力 を変更するロジックを導入するのが良いでしょう。まず別の便利な変数を導入して、if 文 を作成します。
引数の数を調べる
$1
や $2
は引数の位置に応じた値を保持します。別の変数 $#
は、引数の数 を保持します。
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username=$1 echo "Hello $username!" echo "Number of arguments: $#."
$ ./new_script.sh Carol Dave Hello Carol! Number of arguments: 2.
条件分岐
プログラミングにおける条件判断の利用は広大なトピックですから、このレッスンで深くは取り上げません。Bash における条件判断の 書式 に焦点を当て、他のプログラミング言語と異なる点を説明します。
まず、達成したいゴールを説明します。1人のユーザーへの挨拶を出力する単純なスクリプトを作ります。ユーザーが1人ではない場合には、エラーメッセージを出力するようにしましょう。
-
調べたい 条件 は、(コマンドに指定された)ユーザー数です。それは、
$#
変数に格納されています。$#
が1
であることを確認します。 -
もし、条件が 真 であれば、取るべきアクションはそのユーザーに挨拶することです。
-
また、条件が 偽 であれば、エラーメッセージを表示します。
ロジックが整理できたので、そのロジックを実装するための 書式(書き方)に進みましょう。
#!/bin/bash # A simple script to greet a single user. if [ $# -eq 1 ] then username=$1 echo "Hello $username!" else echo "Please enter only one argument." fi echo "Number of arguments: $#."
条件判断のロジックは、if
と fi
の間に置きます。調べる条件はカギ括弧 [ ]
の間に置き、条件が真であるときに実行するアクションは then
の後に置きます。かぎ括弧内やロジック(論理式)に含まれるスペースに注意しましょう。それらのスペースを省略するとエラーになります。
このスクリプトは、あいさつかエラーメッセージを表示し、最後に 引数の数 を表示します。
$ ./new_script.sh Please enter only one argument. Number of arguments: 0. $ ./new_script.sh Carol Hello Carol! Number of arguments: 1.
if
文についてまとめておきましょう。-eq
は、数値の比較 に使います。例の場合は、$#
の値が1と 等しい ことを調べています。比較には次のものも使えます:
-ne
-
等しくない
-gt
-
より大きい
-ge
-
より大きいか等しい
-lt
-
より小さい
-le
-
より小さいか等しい
演習問題
-
シェルに以下を入力しました:
$ PATH=~/scripts $ ls ls: command not found
ls
コマンドは、/bin/ls
にあります。/bin
がPATH環境変数に含まれていないので、コマンドを見つけることができません。-
ユーザーは何をしたのでしょうか?
-
PATH
の現在の値に、新しいディレクトリ~/scripts
を追加するコマンドは何でしょう?
-
-
次のスクリプトについて考えます。ここでは、2番目の条件を調べるために、
elif
を使っています。> /!bin/bash > fruit1 = Apples > fruit2 = Oranges if [ $1 -lt $# ] then echo "This is like comparing $fruit1 and $fruit2!"(参考訳: $fruit1と$fruit2を比べるよ!) > elif [$1 -gt $2 ] then > echo '$fruit1 win!' else > echo "Fruit2 win!" > done
-
マーク
>
が付いた行にはエラーがあります。エラーを修正しましょう。
-
-
前問のスクリプトを
guided1.sh
とします。以下を実行すると何が出力されますか?$ ./guided1.sh 3 0
$ ./guided1.sh 2 4
$ ./guided1.sh 0 1
発展演習
-
2つの引数が渡されていることをチェックして、それらの引数を逆順に表示するスクリプトを書いて下さい。 この例を参考にして下さい(これとは違うコードでも構いませんが、出力は同じになるように):
if [ $1 == $number ] then echo "True!" fi
-
数値ではないものを比較する演算子は何でしょう? インターネットで、この演算子と
-eq
を使う場合の違いを調べて下さい。 -
現在のディレクトリを保持している環境変数があります。
env
を使って、その変数の名前を見つけましょう。 -
問題2と3で学んだことを使って、1つの引数を受け付ける短いスクリプトを書いて下さい。引数が渡されたら、それが現在のディレクトリ名に一致するかを調べます。一致したら
yes
を表示し、しなければno
を表示します。
まとめ
このレッスンでは、次のことを学びました:
-
簡単なスクリプトの作り方と実行方法
-
シェバンを使ってインタープリタを指定する方法
-
スクリプトの中で変数をセットして使う方法
-
スクリプト内で引数を扱う方法
-
if
文の組み立て方 -
数値演算子を使って数値を比較する方法
演習で使用したコマンド:
echo
-
文字列を標準出力に表示する。
env
-
すべての環境変数を標準出力に表示する。
which
-
コマンドの絶対パスを表示する。
chmod
-
ファイルのパーミッションを変更する。
演習で使用した特殊変数
$1, $2 … $9
-
スクリプトに渡された位置引数を保持する。
$#
-
スクリプトに渡された引数の数を保持する。
$PATH
-
システムが使用する実行可能ファイルを収めた一連のディレクトリを保持する。
演習で使用した演算子:
-ne
-
等しくない
-gt
-
より大きい
-ge
-
より大きいか等しい
-lt
-
より小さい
-le
-
より小さいか等しい
演習の解答
-
シェルに以下を入力しました:
$ PATH=~/scripts $ ls ls: command not found
ls
コマンドは、/bin/ls
にあります。/bin
がPATH環境変数に含まれていないので、コマンドを見つけることができません。-
ユーザーは何をしたのでしょうか?
PATHの内容を、ディレクトリ
~/scripts
で上書きしました。PATHに(/bin/lsディレクトリが)含まれていないため、ls
コマンドはもう見つかりません。この変更は現在のセッションにのみ影響し、ログアウトすると変更が元に戻ることに注意しましょう。 -
PATH
の現在の値に、新しいディレクトリ~/scripts
を追加するコマンドは何でしょう?PATH=$PATH:~/scripts
-
-
次のスクリプトについて考えます。ここでは、2番目の条件を調べるために、
elif
を使っています。> /!bin/bash > fruit1 = Apples > fruit2 = Oranges if [ $1 -lt $# ] then echo "This is like comparing $fruit1 and $fruit2!" > elif [$1 -gt $2 ] then > echo '$fruit1 win!' else > echo "Fruit2 win!" > done
-
マーク `>`が付いた行にはエラーがあります。エラーを修正しましょう。
#!/bin/bash fruit1=Apples fruit2=Oranges if [ $1 -lt $# ] then echo "This is like comparing $fruit1 and $fruit2!" elif [ $1 -gt $2 ] then echo "$fruit1 win!" else echo "$fruit2 win!" fi
-
-
以下を実行すると何が出力されますか?
$ ./guided1.sh 3 0
Apples win!
$ ./guided1.sh 2 4
Oranges win!
$ ./guided1.sh 0 1
This is like comparing Apples and Oranges!
発展演習の解答
-
2つの引数が渡されていることをチェックして、それらの引数を逆順に表示するスクリプトを書いて下さい。 この例を参考にして下さい(これとは違うコードでも構いませんが、出力は同じになるように):
if [ $1 == $number ] then echo "True!" fi
#!/bin/bash if [ $# -ne 2 ] then echo "Error" else echo "$2 $1" fi
-
数値ではないものを比較する演算子は何でしょう? インターネットで、この演算子と
-eq
を使う場合の違いを調べて下さい。==
を使うと、文字列 の比較を行います。つまり、両方の変数の文字が正確に一致している場合に、条件が真になります。abc == abc
真
abc == ABC
偽
1 == 1
真
1+1 == 2
偽
文字列比較で数値のテストを行った場合は、予期しない振る舞いとなることがあります。
-
現在のディレクトリを保持している環境変数があります。
env
を使って、その変数の名前を見つけましょう。PWD
-
問題2と3で学んだことを使って、1つの引数を受け付ける短いスクリプトを書いて下さい。引数が渡されたら、それが現在のディレクトリ名に一致するかを調べます。一致したら
yes
を表示し、しなければno
を表示します。#!/bin/bash if [ "$1" == "$PWD" ] then echo "yes" else echo "no" fi