最低限 UNIX / Linux [III]

  1. シェルとシェルスクリプト
    [1.1] シェル(shell)
    [1.2] シェルの役割
    [1.3] 代表的なシェルの紹介
    [1.4] 利用するシェルの確認と変更
    [1.5] bash の機能
    [1.6] シェルスクリプト入門
    [1.7] シェルスクリプト応用
  2. 最低限 vi
    [2.1] vi とは
    [2.2] vi の起動
    [2.3] vi の基礎 - 状態遷移
    [2.4] vi の基礎 - コマンドモード
    [2.5] vi の基礎 - 挿入モード
    [2.6] vi の応用

1. シェルとシェルスクリプト

[1.1] シェル(shell)

シェルとは, ユーザーから入力されたコマンドを解釈し, プログラムを起動するアプリケーションです. 他の OS で機能的に似た役割を持つものとして, Windows95/98では Explorer, Macintosh(MAC)では finder があります. しかし, イメージとしては Windows95/98 の DOS プロンプトや, WindowsNT のコマンドプロンプトの方が近いでしょう.

通常, シェルは login シェルとして, 各ユーザがログインする度に起動され, ログアウトの際に終了します. ログインすると, 端末の上に, 例えば "samson$" と表示されますが, これがシェルが発している「プロンプト」 (ユーザへコマンドの入力を促す記号)です.

UNIX の内部構造をみると, 核となる部分はカーネル(Kernel)と呼ばれ, マルチタスクやファイルシステム, 仮想記憶, 入出力など, OS にとって重要な機能を司っています. ユーザーはカーネルを直接操るのではなく, あらかじめ用意されたインターフェイス(窓口)を通して, カーネルに対してコマンドの実行を要求します. このインターフェイスの部分がシェルであり, つまりシェルはユーザとカーネルの仲立ちをしている プログラムであると言えるでしょう.


[1.2] シェルの役割

シェルには大きく分けて3つの役割があります.

  1. ユーザーインターフェース(コマンド・インタプリタとしてのシェル)
    • プロンプトの表示
    • コマンドの読み込み
    • コマンドの前処理(解釈)
    • コマンドの実行
  2. 環境設定の道具
  3. プログラミング言語(スクリプトを解釈する論理的な制御機能としてのシェル)

[1.3] 代表的なシェルの紹介

主要なシェルには以下のようなものがあります.


[1.4] 利用するシェルの確認と変更

ログイン時に起動されるシェルのことをログインシェルという. 第2回の講義にあった /etc/passwd ファイルの第7フィールドに書かれたシェルが 各ユーザのログインシェルとして使われる. 始めてアカウントをもらったホスト(計算機)では必ずしも自分が使い成れた シェルがログインシェルとなっていない. この場合, ホストに準備されている使い成れたシェルをログインシェルに 変更したほうが良い.

[1.4.1] 自分のログインシェルを調べる方法

$ grep '^username:' /etc/passwd | cut -d: -f 7
$ finger username
$ set

[1.4.2] 自分のログインシェルの変更方法

$ chsh (または ypchsh)

次にログインするときに, 変更したシェルが起動されます. (管理者の設定により無理な場合があります) /etc/shells に記載されているもが変更可能なシェルだが, 実際にそのシェルが存在するのか確認しておくこと (実体が無いとログインできなくなる).


[1.5] bash の機能

[1.5.1] シェルの組み込みコマンド

多くのシェルは, ls や kterm などの独立したコマンドを実行するだけでなく, あらかじめシェルのプログラムに組み込まれているコマンドを幾つか持っています. このようなコマンドは"組み込みコマンド"と呼ばれ, 以下のようなものがあります.

echo, set, unset, alias, history, cd, ...

help コマンドを利用すると, これら組み込みコマンドの, 簡単な説明を得られます.

[1.5.2] シェル変数

bashは, シェル自身の動作を定義する変数を持っています. この, シェルが内部で保持している変数を, "シェル変数"と言います. シェルスクリプトや, コマンドの入力の際に, 値を一時的に格納して後で再利用できるようにするための変数でもあります. 後述する環境変数と違い, シェル以外のプログラムからは参照できません.

基本的に, 変数名をつける際は, 好きなようにつけて構わないのですが, すでに定義されている変数とは重ならないように指定した方が無難です.

例として, プロンプトの表示を変えてみましょう. プロンプトの表示は, シェル変数"PS1"を用いて設定することが出来ます.

$ PS1="\u% "       
inex%
inex% PS1="\d$ "
Fri Nov 26$
Fri Nov 26$ PS1="\s-\v\$ "
bash-2.01$ 

[1.5.3] 環境変数

シェル変数はシェルの中だけで使われる変数ですが, こちらは, アプリケーションやコマンドなどの動作を統一的に制御するために, ユーザーが設定する変数です. 代表的なものとしては, USER, HOST, LANG, DISPLAY, PATH, HOME などがあります.

シェル変数・環境変数には, あらかじめ使い方が決められたものがあり, 変更には注意が必要です.

例として, 以下の4つのコマンドを順番に入力してみてください.

$ export LANG=C
$ man man
$ export LANG=ja_JP.ujis
$ man man

これを実際に試してみると, 一度目の man man と二度目の man man で出力される結果が違うのが, 確認できると思います. 環境変数とはこのように, あらかじめ設定していた文字列によってプログラムの動作を規定する為のものです. (プログラム側で対応していない場合は効果ありません)

[1.5.4] メタキャラクタ(ワイルドカード)

コマンド入力の際に, 複数のファイル名を「ある規則にしたがってまとめて表す」という試みのために用いられる文字のことを, "メタキャラクタ"(ワイルドカード)と言います. メタキャラクタを用いると, タイプする文字数を減らすことができるので, 効率的に入力が行えます.

代表的なメタキャラクタ
メタキャラクタ 意味 用例 用例の意味
* 任意の文字列を表す.
ls /dev/h*
ディレクトリ"/dev/"以下にある1文字目が"h"で始まるファイル全てを 表示せよ.
? 任意の1文字を表す. ?? は任意の2文字になる.
ls -d /??? 
ディレクトリ"/"以下にある3文字からなるディレクトリ全てを 表示せよ.
[ ] [ ] 内に含まれる文字にマッチする. 例えば [a-c]* は abc のいすれかで始まる任意の文字列を表す. []内の先頭に"^"を付けると"否定"と意味になる.
ls -d /[a-c]* 

ls -d /[^a-c]*
ディレクトリ"/"以下にある"a,b,c"のいずれかで始まる(始まらない)ディレクトリを表示せよ.
{ } { }内に含まれる文字列にマッチする. 例えば test.{pl,gif,f} は, test.pl test.gif test.f と入力したことになる.
ls /dev/{hda,hdb}* 
ディレクトリ"/dev/"以下にある"hda,hdb"で始まるファイル全てを 表示せよ.

[1.5.5] エイリアス(別名)機能

あるコマンドに対して, 別名をつけたいときに用います.

alias 別名=コマンド名
alias 別名='オプション付きのコマンド名'

お薦め: rm -irm と変更

[1.5.6] ジョブ制御

ジョブ(Job)とは, ユーザがコンピュータに行なわせる仕事の単位です.

似た意味を持つ言葉に, タスクとプロセスがありますが, この2つは共にマルチプログラミングの目的で, コンピュータがCPU(中央演算処理装置)に行わせる仕事の単位です. ジョブは, 複数のジョブステップから構成されることがありますから, ジョブとタスクは一致するとは限りません.

bash には, 1つのシェルで複数のジョブを切替えながら, 並行して作業を行う機能があります. これをジョブ制御と呼びます.

上記2種のジョブを制御するために, bash には fg, bg, jobs コマンド, そして & が用意されています.

はじめフォアグランドジョブとして起動されたコマンドを, バックグランドジョブに変更するには, (フォアグランドジョブが実行されているために)プロンプトが表示されていない状態の端末で, Ctrl-z(コントロール・キーを押しながら z キーを押す)として, ジョブを中断させます.

$ kterm &
$ xclock
^Z
[2]+  Stopped          xclock

< Ctrl-z を押します
> ジョブ番号[2]の xclock が停止しました

次に jobs と打ち込み, 現在このシェルから実行中のジョブ一覧を表示させます.

$ jobs
[1]-  Running          kterm &
[2]+  Stopped          xclock
> ジョブ番号[1]の kterm が実行中です
> ジョブ番号[2]の xclock が停止中です

xclock より前に kterm が実行されていたので, 上のような表示になります. [ ]の中の数字はジョブの番号を表します. +は "current job", - は"previous job"と呼ばれ, ジョブの切替え対象となる順番を表しています.

bg コマンドを用いると, フォアグランドジョブをバックグランドジョブに切り替えることが出来ます. (bg の後に % ジョブ番号と入力)

$ bg %2
[1]-  Running          kterm &
[2]+  Running          xclock &
> ジョブ番号[1]の kterm が実行中です
> ジョブ番号[2]の xclock が実行中です

逆に, バックグランドジョブをフォアグランドジョブに切り替えるには, fg コマンドを用います. (ジョブ番号の指定の仕方は, bg コマンドと同様)

$ fg %1
kterm

[1.5.7] ファイル, コマンド名の補完機能

bash には, 途中まで打ち込まれた内容を元に, ファイルやコマンドを補完する機能が備わっています. 具体的には, 目的の文字列を何文字か入力し, Tab キーを押します. 複数候補が存在する場合は, Tab キーを2回押すことで, その一覧を表示させることが出来ます. この機能は他にも, シェル変数, ユーザー名, ホスト名なども補完してくれます.


[1.6] シェルスクリプト入門

これまで見てきたように, シェルはユーザとコンピュータの橋渡しをして, ユーザがコマンドを端末から打ち込む毎に, それを解釈し実行します. 一方, [1.2]節で触れたように, 「プログラムが可能である」という, もう一つの面もあります. この, プログラムとして手続きを書き込んだファイルを「スクリプト・ファイル」と言います. スクリプト(script)とは劇の「台本」のことであり, 台本を事前に決めていてそれに沿って行わせるためにこの名前があります.

シェル・プログラミングは通例, B シェルを使って行います*. 具体的にどのようなものか, 簡単な例を見てみましょう.

次のような一連の仕事を行うコマンドを作ることを考えます. (カレントディレクトリは /home/inex/script)

それぞれの働きをするコマンドは
です(Hey! と言われて励まされるかどうかは議論の対象としない).

従って, 次のようなファイルを書こう.

[スクリプトファイル:sample]
#! /bin/sh
date
pwd
echo 'Hey!'
ここで,
$ sh sample
とすると, 次のように出力される.
$ sh sample
1990年9月27日(木) 14:40
/home/inex/script
Hey!

この場合, B シェル(sh)をわざわざ起動して, その引数としてファイル名を指定し, そのファイルを B シェルが解釈して実行しています.

いちいちこのようなことをするのは非能率でもあるので,

$ chmod 744 sample
もしくは
$ chmod u+x sample
とすると, スクリプトのファイル名を入力するだけで実行することができます.
※但し, PATH には注意して下さいね.
$ sample

この場合, 自動的に(自分が使っている login シェル)が, スクリプトファイルを B シェルに解釈させ実行させています.


[1.7] シェルスクリプト応用

[1.7.1] 引数処理

引数とは, 実行時にそのシェルスクリプトに教えて上げたい追加情報であるとも言えます.

たとえば, コマンド ls は, そのままではカレント・ディレクトリ内のファイルをリストアップしますが, 引数として特定のディレクトリを指定すると, 指定されたディレクトリ内のファイルをリストアップします. これはコマンド ls に, 引数として指定したディレクトリについて知りたいんだよ, と追加情報を与えているわけです.

コマンドやシェルスクリプトに引数を指定することを, "引数を渡す"とも表現します. シェルスクリプトに渡された引数を処理したい場合は, $1, $2, ... $9 というシェル変数を利用します.

例えば, 次のような, 引数に渡された単語をそのまま表示するシェルスクリプトを考えてみましょう.

$ script Hello!
#!/bin/sh
echo $1

ここでは, 引数の文字を画面に出力するコマンド echo に, シェル変数 $1 を引数として渡しています. このシェル変数 $1 には, シェルスクリプト script に渡された引数 Hello! が入ります. 結果, 画面には Hello! と表示されるでしょう.

引数を参照するシェル変数は9つまで使うことが出来ます. それぞれ, $1, $2, ... $9 です.

[1.7.2] メタキャラクタの利用

シェルスクリプト内でも, シェルで用意されたメタキャラクタを利用することができます. B シェルのメタキャラクタは以下の通りです.

B シェルのメタキャラクタ
メタキャラクタ 意味
> program >file とすると標準出力を file に切り換える.
2> program 2>file とすると標準エラー出力を file に切り換える.
>> >>file とすると標準出力を file に追加する.
< program <file とすると標準入力として file から読み込む.
| program1|program2 とすると program1 の標準出力を program2 につなぐ.
<<string ヒア・ドキュメント. string と書かれた行が次に現れるまで, この後に続く行を標準入力にする.
* ファイル名の中の長さ0以上の任意の文字列と一致する. (file* とすると, filea, fileb, fileabc はもとより file というのも含まれることになるので注意を要する. )
? ファイル名の中の任意の1文字と一致する.
[ccc] ファイル名の中の ccc のなかの任意の文字と一致する. 0-9,a-z といった範囲指定も許される.
; コマンドの区切り. program1;program2 とすると, まず program1 が実行され, 次に program2 が実行される.
& ;と似ている. ただし, program1 の終了を待たない.
`...` ... にあるコマンド(群)を実行し, その出力は`...` に置換される.
(...) ... のコマンド(群)をサブシェルのなかで実行する.
{...} ... のコマンド(群)をカレントシェルのなかで実行する.
$1, $2, etc $0...$9 はシェルファイルに対する引数に置換される.
$var シェル変数 var の値.
${var} シェル変数 var の値. テキスト部分と連結されたときの混乱を防ぐために ${var} を用いる.
\ エスケープ記号. c がシェルのメタ・キャラクタである場合, \c は c の特別な意味を打ち消し, 単なる文字としての c にする.
'...' ... をそのまま取り出す.
"..." うちに含まれる $,`...`,\ を解釈した後で ... を取り出す.
# 単語の先頭に # があると, その行に書かれた残りの内容はコメントになる.
var=value 変数 var への値 value の代入
p1 && p2 p1 を実行し, もしうまくいったら p2 を実行する.
p1 || p2 p1 を実行し, もしうまくいかなかったら p2 を実行する.

[1.7.3] 処理の流れを制御する - 順序構造

より複雑な処理をこなす場合でも, 大抵は次の3つの処理の組み合わせで表現できます.

順序構造は, 順番にコマンドを実行するものであるから, 特別の制御文は必要ありません.

選択構造には, if , case を使います. 繰り返し構造には, for , while , until を使います.

[1.7.4] 処理の流れを制御する - 選択構造

 if
書式
    if テストコマンド
        then コマンド1
       [else コマンド2]
    fi
  
解説

if 文は if と fi で対になって, いれ子を形成しています. if に続く「テストコマンド」をまず実行し, それが「真」(0以外の値)を返したら then に続くコマンドが, 「偽」(0)を返したら else に続くコマンドが実行されます.

else とコマンド2の部分は省略可能です. また, コマンド1とコマンド2には, 複数のコマンドからなるコマンド列を記述することも出来ます. if....fi のいれ子は多重に重ねることが出来ます.

if に続くテストコマンドの文と then に続くコマンド1の文は普通改行します. どうしても1行にまとめて書きたければ, if テストコマンド; then コマンド1 のようにセミコロンを挟みます.


 case
書式
    case 文字 in
        パターン1)  コマンド1  ;;
        パターン2)  コマンド2  ;;
          ・
          ・
    esac
  
解説

パターンには, メタキャラクタ ?, *, [-] を使ったパターンマッチング機能が使えます. case の左に指定される「文字」はパターン1から順次比較され, 一致するとそのパターンに対応するコマンド列(括弧以降)が実行されます. コマンド列の終わりは2重のセミコロン;;で区切られます.

[1.7.5] 処理の流れを制御する - 繰り返し構造

 while, until
書式
    while コマンド; do
      コマンド
    done
  
解説

while は, while の左に書かれたコマンドが真(0以外の値)を返す限り, do と done の間に書かれたコマンド(列)を実行し続けます.

while の代わりに until と書くと, その左に指定されたコマンドが偽(0)を返す限り, do と done の間に書かれたコマンド(列)を実行し続けます.

[1.7.6] 処理の流れを制御する - 例

■与えられた引数の数を表示する.
#!/bin/sh
case $# in
    0) echo '引数なし' ;;
    1) echo '引数一つ' ;;
    2) echo '引数二つ' ;;
    *) echo '引数三つ以上' ;;
esac

※ $# は与えられた引数の数を表すシェル変数です.

■引数に指定された複数のファイル名に対し, 実在する物のみファイル名を表示する.
#!/bin/sh
while test $# -gt 0
do
    if test -f $1; then
        echo $1
    fi
    shift
done

※ test は, それに続く式を評価して値を返すコマンドです. 与えるオプションに応じて, 数値に関するテスト, ファイルの型に関するテスト, 文字列に関するテストを行うことが出来ます. 例えば, test -f [ファイル名]とすると, ファイルが存在すれば真, 存在しなければ偽を返します.

■数字の1から9を表示する.
#!/bin/sh
number=1
while test $number -ne 10
do
    echo $number
    number=`expr $number + 1`
done

※expr コマンドはその引数を1つの式として評価し, その結果を表示します. これを使うと UNIX で簡単な計算を行い, シェル・スクリプトの中に算術演算処理を含めることが出来ます. なお, number の式の右辺はクオーテーション(')ではありません. 注意して下さい. 気になる人は B シェルのメタキャラクタの欄を参照して下さい.

より詳しい情報は man sh として B シェルのマニュアルを参照することで得ることも出来ましょう. 分からないコマンドや単語は, 困ってないでどんどん調べようね.




>> 次ページへ

最終更新日: 2000/10/26(河野仁之) Copyright © 2000 inex