シェル・スクリプト

システム・プログラム

                                       電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro-2001/2001-06-18
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.is.tsukuba.ac.jp/~yas/index-j.html

■今日の重要な話

■スクリプト言語

コンピュータ言語の種類 スクリプト言語とは、アプリケーション本体ではなく、アプリケーションの細 かな動作を変更したり、アプリケーション本体を変更することなく機能を追加 したりするために使われる言語。

■インタプリタとスクリプト

スクリプト言語は、多くの場合、インタプリタで実行される。

プロセスはインタプリタの実行形式(機械語)から作られ、インタプリタのソー ス・プログラムは、そのプロセスが読み込む単なるデータとなる。

Unix には、スクリプトを簡単に実行する仕組みとして #!がある。

◆インタプリタ/bin/cat

標準入力を標準出力に出力する cat コマンド 「#!」の働きを調べる。 cat は「プログラムを表示する」インタプリタである。
----------------------------------------------------------------------
% cat > run-cat [←]
#!/bin/cat
hello
^D
% chmod +x run-cat [←]
% ls -l run-cat [←]
-rwxr-xr-x  1 yas            20 Sep 13 04:11 run-cat
% []
----------------------------------------------------------------------
実行する。
----------------------------------------------------------------------
% ./run-cat [←]
#!/bin/cat
hello
% []
----------------------------------------------------------------------
これは次のように実行したものと同じになります。
----------------------------------------------------------------------
% /bin/cat ./run-cat [←]
#!/bin/cat
hello
% []
----------------------------------------------------------------------

◆一般のインタプリタ

「#!」の形式は次のようになる。

インタプリタの実行形式の絶対パス名: /dir/interpreter

そのソース・プログラムのファイル名: run

run の内容:

----------------------------------------------------------------------
#!/dir/interpreter arg-1 arg-2 arg-3
<以下、プログラム>
----------------------------------------------------------------------
このファイルに実行可能属性を付ける(chmod +x)と、 ファイル名を入力して実行することができる。
----------------------------------------------------------------------
% chmod +x ./run [←]
% ./run arg-a arg-b arg-c [←]
<実行結果>
----------------------------------------------------------------------
これは、次のようにインタプリタを起動したものと同じ結果になる。
----------------------------------------------------------------------
% /dir/interpreter ./run 'arg-1 arg-2 arg-3' arg-a arg-b argc [←]
----------------------------------------------------------------------
./run ファイルに書いた引数は、シェルから実行する時場合には、 ''で囲まれた時と同じような形式でインタプリタのプロ セスへの引数として渡される。

◆#!の解釈

#!」行は、Unixのカーネルが解釈する。 シェルが、ファイルの先頭を読み、指定されたインタプリタを起動するのでは ない。「#!」行では、シェル変数、環境変数、エイリアスはつ かえない。

#」から始まる行がコメントであると都合がよい。

awksed のように、プログラムが含まれたファイルを指定 する時に -f (program file) オプションが必要なものは、次の ように、この行に -f を付ける。

----------------------------------------------------------------------
#!/usr/bin/awk -f
{ print }
----------------------------------------------------------------------

■シェル

シェルはインタプリタであり、シェル・スクリプトはシェル・インタプリタの プログラムである。

シェル・スクリプトの先頭の #!/bin/sh#!/bin/csh は、 そのインタプリタを起動するという意味である。

◆csh

O2での標準のログインシェルは、tcsh。

tcsh は、csh に terminal での編集機能や補完機能を付けたもの。シェル・ スクリプトを書く時には、多くのシステムで備わっている /bin/csh を使うこ とが多い。

◆cshスクリプトの作り方

まず端末から実行してみる。 端末から打ち込んだものを結果を、ファイルに保存する。

----------------------------------------------------------------------
% gcc -I/usr/local/include/ file1.c -llib1 -llib2 -o prog [←]
% ^p [←]
% echo gcc -I/usr/local/include/ file1.c -llib1 -llib2 -o prog > run [←]
% csh run [←]
% []
----------------------------------------------------------------------
''
tcsh の機能で、^p (Control+P) で1行戻して、echo でファイルに落とす。 「|」があれば、'' でくくる。csh の引数にファイル名を与えて実行できる。

いちいち csh と打たないでもいいようにするには、chmod する。

----------------------------------------------------------------------
% chmod +x run [←]
% ./run [←]
% []
----------------------------------------------------------------------
#! がなければ、execve() システム・コールがエラーになる。その場合、tcsh が sh か csh (先頭が#の時)を実行する。

必要なら、エディタで「#!/bin/csh」か「#!/bin/sh」を入れる。

数行にわたるものの場合、history コマンドを使う。

----------------------------------------------------------------------
% history [←]
% history | tail -5 > run [←]
% mule run [←]
----------------------------------------------------------------------

「#!/bin/csh -f」と、-f を付けた方が、~/.cshrc を読み込まないので起動 が速い。ただし、~/.cshrc での設定は効かないことがある。環境変数は、今 の状態が引き継がれる。

◆デバッグ

-x オプションを付けて実行すると、画面にスクリプトを表示さながら実行す る。
% csh -f -x run [←]

-n オプションを付けて実行して、構文のチェックだけ行う。

シェルに1行ずつ与えて実行してみる。

◆cshスクリプトでよく使われる機能

入出力の切替え
----------------------------------------------------------------------
<file		標準入力をfileに切替える。
>file		標準出力をfileに切替える。fileの内容が上書きされる。
>>file		標準出力をfileに切替える。fileの末尾に追加される。
>&file		標準出力と標準エラーをfileに切替える。
p1 | p2		p1 の標準出力をパイプに、p2 の標準入力をパイプに
p1 |& p2	p1 の標準出力と標準エラーをパイプに、p2 の標準入力をパイプに
p1 <<EOF	p1 の標準入力を、EOF という文字がが現れるで。
...
EOF
----------------------------------------------------------------------
引数

----------------------------------------------------------------------
$1 $2 $3 ...			n番目の引数
$argv[1] $argv[1] $argv[2] ...	n番目の引数
$0				プログラムの名前
$*				全引数
$#argv				引数の数
shift				引数のシフト
----------------------------------------------------------------------
シェル変数
----------------------------------------------------------------------
set var=value			シェル変数の設定
set var="value1 value2"		シェル変数の設定
set var=(value1 value2)		シェル変数の設定
unset var			シェル変数の解除
$var "$var" ${var} $var[index]	シェル変数の利用
$$				PID
@ var=式			式の計算
----------------------------------------------------------------------
環境変数
----------------------------------------------------------------------
setenv var value		環境変数の設定
setenv var "value1 value2"	環境変数の設定
unsetenv var			環境変数の解除
$var "$var" ${var}		環境変数の利用
----------------------------------------------------------------------
コメント

----------------------------------------------------------------------
#		コメント
----------------------------------------------------------------------
引用符、エスケープ

----------------------------------------------------------------------
"..."		$、``、\を解釈した文字列
'...'		文字列
`...`		...にあるプログラムを実行
\c		cそのもの(\* や \")で使う
\改行		行末のキャンセルX(1行で書けない時の継続行)
----------------------------------------------------------------------
ファイル名、文字列の展開

----------------------------------------------------------------------
 *		0文字以上の任意の文字列
?		任意の1文字
[abc]		[]の中の任意の1文字
x{a,b,c}z	{}の中の展開。xaz xbz xczの意味。
~user		ユーザのホーム・ディレクトリ
----------------------------------------------------------------------
if文

----------------------------------------------------------------------
if( ... ) cmd

if( ... ) then
  cmd 
endif

if( ... ) then
  cmd
else
  cmd
endif

if( ... ) then
  cmd
else if( ... ) then
  cmd
else if( ... ) then
  cmd
else
  cmd
endif

----------------------------------------------------------------------
ループ

----------------------------------------------------------------------
while( ... )
   cmd
end

foreach var (w1 w2 w3 w4)
   cmd $var
end
----------------------------------------------------------------------
switch 文

----------------------------------------------------------------------
switch( string )
case label:
   breaksw
default:
   breaksw
endsw
----------------------------------------------------------------------
その他

----------------------------------------------------------------------
goto label
...
label:

exit 数			終了
exec cmd		fork しないで自分自身で cmd を実行。戻ってこない。
source script		fork しないで自分自身で script を実行。終了後、戻ってくる。
----------------------------------------------------------------------
if 文、while 文の条件式、「@ var =式」の式としては、次のようなものが使 える。

----------------------------------------------------------------------
+ - * / % << >>		計算(数)
< > <= >=		比較(数)
== !=			比較(数、文字列)
=~ !~			パタンマッチ。if( aaa =~ a*b ) のように使う。
~ &  |  ^ 		ビット演算
! && ||			論理演算
-r filename		読み込み可能
-w filename		書き込み可能
-x filename		実行可能
-e filename		ファイルが存在する
-f filename		普通のファイルが存在する
-d filename		ディレクトリが存在する
-z filename		ファイルが空(長さが zero)
-o filename		ファイルの owner の時1
{ command }		コマンドの実行。成功(exit(0))すれば 1。
( )			式のグループ化
----------------------------------------------------------------------
実行構造

----------------------------------------------------------------------
p1 ; p2		p1 の実行後 p2 の実行。
p1 &		p1 のバックグランド実行(waitしない)
p1 && p2	p1 を実行して、失敗したら終わり。成功したら p2 を実行。
p1 || p2	p1 を実行して、成功したら終わり。失敗したら p2 を実行。
----------------------------------------------------------------------
わかりにくいシェル変数と環境変数

----------------------------------------------------------------------
シェル変数term		変更すると環境変数 TERM にも反映される。
シェル変数path		変更すると環境変数 PATH にも反映される。
シェル変数home		変更すると環境変数 HOME にも反映される。
----------------------------------------------------------------------

◆shスクリプトでよく使われる機能

シェル・スクリプトは、csh よりも sh の方が便利なことがある。

入出力の切替え

----------------------------------------------------------------------
<file		標準入力をfileに切替える。
>file		標準出力をfileに切替える。fileの内容が上書きされる。
2>file		標準エラーをfileに切替える。fileの内容が上書きされる。
n>file		ファイル記述子nをfileに切替える。fileの内容が上書きされる。
n<file		ファイル記述子nをfileに切替える。
>>file		標準出力をfileに切替える。fileの末尾に追加される。
2>&1		標準エラーを標準出力と同じにする。
<&-		標準入力を閉じる。
>&-		標準出力を閉じる。
p1 | p2		p1 の標準出力をパイプに、p2 の標準入力をパイプに
p1 <<EOF	p1 の標準入力を、EOF という文字がが現れるで。
...
EOF
p1 <<EOF	p1 の標準入力を、EOF という文字がが現れるで。
...		変数を展開しない。
EOF'
----------------------------------------------------------------------
'''
引数

----------------------------------------------------------------------
$1 $2 $3 ...			n番目の引数
$0				プログラムの名前
$*				全引数
$@				全引数
"$@"				全引数(展開しない)
$#				引数の数
shift				引数のシフト
read var			標準入力から1行入力
----------------------------------------------------------------------
シェル変数。csh とは異なり、setは不要。
----------------------------------------------------------------------
var=value			シェル変数の設定
var="value1 value2"		シェル変数の設定
unset var			シェル変数の解除(古いshにはない)
$var "$var" ${var}		シェル変数の利用。
$$				PID
----------------------------------------------------------------------
sh には、「$var[index]」や「@ var=式」はない。

sh の環境変数は、シェル変数のうち export したもの。

----------------------------------------------------------------------
var=value; export var		環境変数の設定
var="value1 value2"; export var	環境変数の設定
unset var			環境変数の解除
$var "$var" ${var}		環境変数の利用
----------------------------------------------------------------------
コメント

----------------------------------------------------------------------
#		コメント
----------------------------------------------------------------------
引用符、エスケープ

----------------------------------------------------------------------
"..."		$、``、\を解釈した文字列
'...'		文字列
`...`		...にあるプログラムを実行
\c		cそのもの(\* や \")で使う
\改行		行末のキャンセルX(1行で書けない時の継続行)
----------------------------------------------------------------------
ファイル名、文字列の展開

----------------------------------------------------------------------
 *		0文字以上の任意の文字列
?		任意の1文字
[abc]		[]の中の任意の1文字
----------------------------------------------------------------------
sh では、「~user」はない。bash にはある。

if文


----------------------------------------------------------------------
if [ ... ]
then
  cmd 
fi

if [ ... ]
then
  cmd
else
  cmd
fi

if [ ... ]
then
  cmd
elif [ ...  ]
  cmd
elif [ ...  ]
  cmd
else
  cmd
fi
----------------------------------------------------------------------
'[' は、実は、そういう名前のコマンドである。マニュアルは、test で参照 できる。

----------------------------------------------------------------------
% ls -l /usr/bin/[ [←]
lrwxr-xr-x    1 root     sys           15   2月  4日   1998年  /usr/bin/[ -> ../../sbin/test
% []
----------------------------------------------------------------------
'[' コマンドの他に、普通のコマンドを書くこともできる。

----------------------------------------------------------------------
if cmd
then
  cmd
else
  cmd
fi
----------------------------------------------------------------------
if の cmd が exit 0 すると true、それ以外だと false となる。

if と同じ行に then を書く時には、「;」を付ける。


----------------------------------------------------------------------
if [ ... ] ; then
  cmd 
fi
----------------------------------------------------------------------

ループ


----------------------------------------------------------------------
while [ ...  ]
do
   cmd
done

for var in w1 w2 w3 w4
do
   cmd $var
done
----------------------------------------------------------------------
switch 文
----------------------------------------------------------------------
case string in
pattern1) cmd;;
pattern2) cmd;;
 *) cmd;;
esac
----------------------------------------------------------------------
その他

----------------------------------------------------------------------
exit 数			終了
exec cmd		fork しないで自分自身で cmd を実行。戻ってこない。
. script		fork しないで自分自身で script を実行。終了後、戻ってくる。
----------------------------------------------------------------------
'[' (test) コマンドで使える式

----------------------------------------------------------------------
-eq -gt -ge -lt -gt -le -ne 比較(数)
= !=			比較(文字列)
! -a -o			論理演算
-r filename		読み込み可能
-w filename		書き込み可能
-x filename		実行可能
-e filename		ファイルが存在する
-f filename		普通のファイルが存在する
-d filename		ディレクトリが存在する
-s filename		ファイルが空でない
str			空でなければ真。"$var" の形式で使うのが普通。
( )			式のグループ化
----------------------------------------------------------------------
実行構造

----------------------------------------------------------------------
p1 ; p2		p1 の実行後 p2 の実行。
p1 &		p1 のバックグランド実行(waitしない)
p1 && p2	p1 を実行して、失敗したら終わり。成功したら p2 を実行。
p1 || p2	p1 を実行して、成功したら終わり。失敗したら p2 を実行。
( cmd )		fork して子プロセスで cmd を実行する。cd したい時など使う。
----------------------------------------------------------------------

◆shの関数定義

最近の sh や bash では、関数が定義できる。

----------------------------------------------------------------------
#!/bin/sh

ls_l()
{
	ls -l $*
}

ls_l $*
----------------------------------------------------------------------

◆foreachとfor

foreach や for は、引数 $*、全てのファイル *、バック・クォート `` といっしょに使うことが多い。

----------------------------------------------------------------------
% ls [←]
file1  file2  file3
% foreach f (*) [←]
foreach? 
----------------------------------------------------------------------

----------------------------------------------------------------------
% sh [←]
$ for f in * [←]
> do[←]
> echo $f[←]
> done[←]
file1
file2
file3
$ []
----------------------------------------------------------------------
* の代りに、$* を使えば引数、`cat file` を使えば、file の内容ついてループを実行することができる。

◆計算

数の計算が必要な時には、csh, tcsh では、@、sh では、expn を使う。

----------------------------------------------------------------------
% @ x = 1 + 2 [←]
% echo $x [←]
3
% []
----------------------------------------------------------------------

----------------------------------------------------------------------
% sh [←]
$ x=`expr 1 + 2` [←]
$ echo $x [←]
3
$ []
----------------------------------------------------------------------

◆ループの中での文字列の結合


----------------------------------------------------------------------
% set x="" [←]
% foreach i (a b c) [←]
foreach? set x="$x""$i"[←]
foreach? end[←]
% echo $x [←]
abc
% []
----------------------------------------------------------------------

◆コマンドの実行結果

if 文では、コマンドが成功したか失敗したかを調べることができる。 exit(0); した時に、成功(true)、それ以外の時に失敗(false) と扱う。

% if ( { /usr/bin/true } ) echo ok [←]
ok
% echo $status [←]
0
% if ( { /usr/bin/false } ) echo ok [←]
% echo $status [←]
255
% []
csh では、$status でも調べられる。

◆ls の動き

ls コマンドは、標準出力が端末ではない(パイプやファイル)の場合、結果 を縦に並べて出力する。

----------------------------------------------------------------------
% ls [←]
file1  file2  file3
% ls | cat [←]
file1
file2
file3
% []
----------------------------------------------------------------------
強制的に縦に並べるオプション ls -1 や横に並べるオプション ls -C もある。

◆grep

grep (egrep, fgrep) は、ファイルの内容を検索し、引数で与えられたパタン が見つかった行を出力するコマンドである。次の例は、「UTMP_FILE」 または、「WTMP_FILE」という文字列を含む行を、ファイル /usr/include/utmp.h から探し、結果を表示している。

----------------------------------------------------------------------
% egrep '[UW]TMP_FILE' /usr/include/utmp.h [←]
#define UTMP_FILE       "/var/adm/utmp"
#define WTMP_FILE       "/var/adm/wtmp"
% []
----------------------------------------------------------------------
一般的には、次のようになる。
% egrep pattern file1 file2 ... [←]
pattern としては、次のような正規表現(regular expressions)が使える。
.
任意の1文字
.*
任意の1文字が0回以上繰り返したもの
A*
文字Aが0回以上繰り返したもの
A*
文字Aが0回以上繰り返したもの
^
行の先頭
$
行の末尾
[a-m]
文字aから文字mまでの1文字
[A-Za-z0-9]
アルファベットと数字
\c
c そのもの。\. や \*, \$ などで使う。
patter1|patter2
patter1、または、patter2 (egrepのみ)
シェルで「*」と書く所は、grep では「'.*'」と書く点に注意する。 たとえば、 シェルで「*.[ch]」と書く所は、grep では「'.*\.[ch]'」と書く。

-v オプションを付けると、マッチしなかった行が表示される。

-s オプションを付けると、パタンを含むか含まないかの判定だけを行い、画 面に結果を表示しない。シェル・スクリプトの if 文の中で使う。

「*」、「$」、「[]」など、シェルが展開してしまう文字をパタンとして指定 する時には、シングル・クォート「''」で括る。

egrep は、機能が拡張され、アルゴリズムも改良されていて速い。

■path

情報学類教育用計算機では、標準で ~/bin がpath シェル変数(PATH 環境変数)に含まれるようになっている。自分で作成したプログラムやスクリ プトを、~/bin に置くと./ などで実行する必要はない。 ただし、ファイルを作成し、chmod +x した後で、1度だけ rehash コマンド を打つ必要がある。
% mule ~/bin/newcommand [←]
% chmod +x ~/bin/newcommand [←]
% rehash [←]
% newcommand [←]
% mule ~/bin/newcommand [←]
% newcommand [←]
% mule ~/bin/newcommand [←]
% newcommand [←]
% []
chmod も rehash も、シェルごとに1度だけやればよい。kterm をたくさん開 いていた時には、それぞれのシェルでrehash コマンドを事項する。

rehash は、新しいシェルが実行される時には自動的に行われている。次にロ グインした時、chmod +x した後に開いた kterm ではrehash を実行する必要 はない。

■練習問題と課題

★練習問題 68 C言語のソース・プログラムだけを表示するls

与えられたディレクトリのC言語のソース・プログラムだけを表示するシェル・ スクリプトを作りなさい。

----------------------------------------------------------------------
% ls-c ~/syspro-2001/file/ [←]
fd-print.c
file-copy.c
mmap-head.c
stdio-thru.c
utmp-print.c
wtmp-last10.c
ystat.c
ystat.h
----------------------------------------------------------------------
ヒント:ls の結果から、egrep で .c や .h で終わるものを抜き出す。ある いは、シェルのパタン・マッチの機能を使って、*.c や *.h だけを取り出す。

余裕があれば、-l などのオプションが付けられるようにしなさい。

★練習問題 69 「.」から始まるファイルだけを表示するls

ls コマンドや csh, tcsh, sh の * では、「.」から始まるファイルは表示 されない。逆に「.」から始まるファイルだけを表示する ls コマンドを 作りなさい。

----------------------------------------------------------------------
% ls-dot ~ [←]
.
..
.cshrc
.emacs
.login
% []
----------------------------------------------------------------------
ヒント:ls に -a オプションを付けると、全てのファイルを表示する。 先頭が「.」のものを抜き出す。

csh, tcsh, sh で 「.*」と指定して探す方法もある。

余裕があれば、-l などのオプションが付けられるようにしなさい。

★練習問題 70 ディレクトリだけを表示するls

ディレクトリだけを表示する ls コマンドを作りなさい。

----------------------------------------------------------------------
% ls-dir ~ [←]
Mail
lib
syspro
tmp
% []
----------------------------------------------------------------------
ヒント:ls -l で、d から始まる行だけを抜き出す。あるいは、csh の -d や test の -d でディレクトリだけを選びだす。

できるだけ上のようにディレクトリの名前だけ表示するようにしなさい。 モードや日付は表示しないようにしなさい。

余裕があれば、-l などのオプションが付けられるようにしなさい。

★練習問題 71 ファイルの大きさの順に表示するls

ls -l は、ファイルの名前の順に表示する。これを、ファイルの大きさの順に 表示するシェル・スクリプトを作りなさい。

----------------------------------------------------------------------
% ls-size ~yas/syspro-2001/cc/ [←]
-rwxr-xr-x    1 yas      lab        20504   4月 22日 18時56分  byte-order
-rwxr-xr-x    1 yas      lab        19820   6月 17日 18時54分  segv
-rwxr-xr-x    1 yas      lab        12436   6月  3日 21時16分  vaddr-print
-rw-r--r--    1 yas      lab          951   6月  3日 21時15分  vaddr-print.c
-rw-r--r--    1 yas      lab          366   4月 22日 21時11分  byte-order.c
drwxr-xr-x    2 yas      lab           56   6月  3日 21時15分  RCS
lrwxr-xr-x    1 yas      lab           36   4月 15日 19時55分  errno-perror.c -> ../../syspro1-1998/cc/errno-perror.c
lrwxr-xr-x    1 yas      lab           29   4月 15日 19時55分  today.c -> ../../syspro1-1998/cc/today.c
lrwxr-xr-x    1 yas      lab           28   4月 15日 19時55分  segv.c -> ../../syspro1-1998/cc/segv.c
総ブロック数 150
% []
----------------------------------------------------------------------
ヒント:ls -l の出力を sort コマンドでソートする。 ソートは、第5フィールド(-k5)で行う。

★練習問題 72 ファイルの行数の順に表示するwc

wc コマンドは、ファイルの行数、単語数、バイト数を表示する。wc コマンド は、引数で与えられた順に表示する。これを行数の順に表示するようなスクリ プトを作りなさい。

----------------------------------------------------------------------
% wc-lines *.c [←]
            73           181          1166 pipe-rw-dup.c
            65           155          1015 pipe-rw-nodup.c
            57           161          1217 proc-uid-print.c
            50            98           793 setjmp-longjmp.c
            39           100           920 signal-int.c
            38           112           842 proc-create.c
            21            49           483 exec-date.c
            20            50           419 cont-1.c
            20            50           419 cont-0.c
            16            52           447 env-print.c
            15            57           460 arg-print.c
            14            24           267 fork-hello.c
            12            32           300 cont-2.c
% []
----------------------------------------------------------------------
この課題では、合計は表示されなくてもよい。

ヒント:wc の出力を sort コマンドでソートする。foreach か for で1つず つ wc コマンドを実行して、全体の結果を sort するか、引数 $* で wc した 後、sort して、合計の行を削る。

★練習問題 73 使っているメモリのサイズが大きいプロセスの表示

ps -le では、全プロセス(-e)が、サイズなどの情報も含めて(-l)表示される。

----------------------------------------------------------------------
% ps -le  [←]
  F S   UID   PID  PPID  C PRI NI  P    SZ:RSS      WCHAN TTY     TIME CMD
 39 S     0     0     0  0  39 RT  *     0:0     80268270 ?       0:40 sched 
 b0 S     0     1     0  0  39 20  *    98:42    8027eb80 ?       0:15 init 
119 S     0     2     0  0  39 RT  *     0:0     80267c30 ?       2:06 vhand 
119 S     0     3     0  0  39 RT  *     0:0     802678a8 ?       1:09 bdflush 
...
 b0 S     0 11771   684  0  60 20  * 11480:1622  8053853c ?      22:26 Xsgi 
 b0 S     0 22729   684  0  60 20  *   862:140   80538474 ?       0:00 xdm 
 90 Z  1231 24304 24302  0   0 -   -     - -            - -       0:00 <defunct> 
% []
----------------------------------------------------------------------
そのうち、メモリのサイズ(SZ)が大きいプロセスを 10 個だけ表示するシェル・ スクリプトを作りなさい。

----------------------------------------------------------------------
% ps-sz-top10 [←]
 b0 S     0 11771   684  0  60 20  * 11480:1622  8053853c ?      22:26 Xsgi 
 b0 S     0   548   547  0  60 20  *  2678:1619  805384e0 ?       0:30 jserver 
 b0 S  1231 24145 23879  0  60 20  *  2114:909   80538494 pts/1   0:01 emacs 
 b0 S     0 24250 23879  0  60 20  *  1166:653   80538538 pts/1   0:01 kterm 
 b0 S 40508 24517 24509  0  28 20  *  1044:766   8027f5a0 pts/3   0:00 mnews 
 b0 S     0 22729   684  0  60 20  *   862:140   80538474 ?       0:00 xdm 
 b0 S     0   684     1  0  60 20  *   854:75    80538500 ?       0:01 xdm 
 b0 S 40508 24509 24507  0  39 20  *   740:336   8027eb80 pts/3   0:01 tcsh 
 b0 S  1231 24251 24250  1  39 20  *   704:437   8027eb80 pts/2   0:01 tcsh 
 b0 S  1231 23879 23876  0  39 20  *   703:321   8027eb80 pts/1   0:01 tcsh 
% []
----------------------------------------------------------------------
ヒント:SZ の順(第10フィールド)に sort して head する。

余裕があれば、RSS の順、CPU 時間の順に表示するスクリプトを作りなさい。

類似のことを実行するプログラムとして top がある。

★練習問題 74 バックアップ・ファイルとのdiff

emacs (mule) は、ファイルを保存する時に、1つの前のバージョンを「~」を 付けて保存する。そのようなファイルを見つけて、オリジナルのファイルと diff コマンドで比較するようなスクリプトを作りなさい。

% diff-backup *.c [←]
% []
ヒント:foreach または for 文で、引数のファイルについて、 "$file"~ のような名前のファイルが存在するかを調べる。 存在すれば、diff コマンドで表示する。

余裕があれば、比較しているファイルの名前を表示したり、ファイルごとに停 止する、引数を取る、diff に対するオプションを取る、などの工夫をしなさ い。

★練習問題 75 小文字のファイル名への変更

Windows 系のコンピュータから Unix へファイルをコピーすると大文字のファ イル名になってしまうことがある。そのようなファイル名を全て小文字にする ようなスクリプトを書きなさい。


% mv-lower [A-Z]* [←]

ヒント:ファイル名を echo して、tr で小文字にして、それを `` でシェル変数に入れる。元の名前から小文 字の名前に mv で変える。

余裕があれば、大文字と小文字を変換することで、ファイルが上書きされる時 には警告を出したり、ユーザに問い合わせたりするようにしなさい。

★練習問題 76 パタンを含むファイルの名前の表示

特定のパタンを含むファイルの名前を表示するプログラムをつくりなさい。た とえば、次の例では、printf というパタンを含むファイルの一覧を表示して いる。

% find-pattern printf *.c [←]
arg-print.c
env-print.c
signal-int.c
% []
ヒント:egrep -s で調べて、含んでいた時だけファイル名を表示する。

★練習問題 77 カウント・ダウン

秒単位でカウント・ダウンをするようなシェル・スクリプトを作りなさい。

% countdown  [←]
5
4
3
2
1
0
% []
ヒント:foreach か for のリストに数を並べる。 sleep 1 で、1ごとに止める。

余裕があれば、引数で何秒数えるかを指定できるようにしなさい。


% countdown 3 [←]
3
2
1
0
% []

★練習問題 78 目覚まし時計

目覚まし時計になるようなシェル・スクリプトを作りなさい。

% wakeup 12:00 [←]
^G^G^G
% []
ヒント:date コマンドの結果から何秒 sleep するればよいかを 計算する。計算には、csh の場合は、@ 、sh の場合には、expr を使う。

音を鳴らすのは、画面に '^G' (Control+G) 、または、'\07' をecho すれば よい。'^G' は、mule では、'^Q^G' (C-q C-G)、つまり、Control+Q に続き Control+G と打てば入る。

この課題では、at コマンドを使ってはいけない。

★練習問題 79 ファイルのn行目からm行目までの表示

引数として2つの数 n, m 、および、ファイル名を取り、そのファイルの n 行めから m 行目までを表示するシェル・スクリプトを作りなさい。 たとえば、次の例では、ファイルの 10 行目から 20 行目までを表示する。

% show-n-m 10 20 filename [←]
ヒント:head コマンドと tail コマンドを組み合わせて使う。 head も tail も、表示する行数を与えることができる。

余裕があれば、-n オプションを付けなさい。これは、ファイルに行番号を振 るものである。nl コマンドを使うとよい。(cat -n が使えるシステムもある。)

★練習問題 80 lastコマンドの加工

last コマンドで、特定のユーザだけを表示するようにしなさい。

% last-buddy user1 user2 user3 [←]
user1    ttyq2        fw.accsnet.ne.jp      Mon Jun 18 01:36   still logged in
user2    ttyq0        shimizu@130.158.124.1 Mon Jun 18 01:35 - 01:36  (00:00)
user3    ftp13822     UNKNOWN@mars27.sk.tsu Mon Jun 18 01:35   still logged in
user3    ftp14128     UNKNOWN@mars27.sk.tsu Mon Jun 18 01:35   still logged in
user2    ttyq1        prospect.hlla.is.tsuk Mon Jun 18 01:30   still logged in
...
% []
ヒント:last の結果を egrep でより分ける。この時、egrep の引数には、シェ ル・スクリプトの引数として与えらた文字列を、'|'で繋いだものを与える。

★練習問題 81 シェル・スクリプト自由課題

その他、上であげた課題と同程度に難しい課題を自由に設定して解きなさい。