システム・プログラム 電子・情報工学系 新城 靖 <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 には、スクリプトを簡単に実行する仕組みとして #!がある。
実行する。---------------------------------------------------------------------- % 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のカーネルが解釈する。
シェルが、ファイルの先頭を読み、指定されたインタプリタを起動するのでは
ない。「#!
」行では、シェル変数、環境変数、エイリアスはつ
かえない。
「#
」から始まる行がコメントであると都合がよい。
awk
や sed
のように、プログラムが含まれたファイルを指定
する時に -f
(program file) オプションが必要なものは、次の
ように、この行に -f
を付ける。
---------------------------------------------------------------------- #!/usr/bin/awk -f { print } ----------------------------------------------------------------------
シェル・スクリプトの先頭の
#!/bin/sh
や
#!/bin/csh
は、
そのインタプリタを起動するという意味である。
tcsh は、csh に terminal での編集機能や補完機能を付けたもの。シェル・ スクリプトを書く時には、多くのシステムで備わっている /bin/csh を使うこ とが多い。
---------------------------------------------------------------------- % gcc -I/usr/local/include/ file1.c -llib1 -llib2 -o progtcsh の機能で、^p (Control+P) で1行戻して、echo でファイルに落とす。 「|」があれば、'' でくくる。csh の引数にファイル名を与えて実行できる。% ^p
% echo gcc -I/usr/local/include/ file1.c -llib1 -llib2 -o prog > run
% csh run
%
---------------------------------------------------------------------- ''
いちいち csh と打たないでもいいようにするには、chmod する。
---------------------------------------------------------------------- % chmod +x run#! がなければ、execve() システム・コールがエラーになる。その場合、tcsh が sh か csh (先頭が#の時)を実行する。% ./run
%
----------------------------------------------------------------------
必要なら、エディタで「#!/bin/csh」か「#!/bin/sh」を入れる。
数行にわたるものの場合、history コマンドを使う。
---------------------------------------------------------------------- % history% history | tail -5 > run
% mule run
----------------------------------------------------------------------
「#!/bin/csh -f」と、-f を付けた方が、~/.cshrc を読み込まないので起動 が速い。ただし、~/.cshrc での設定は効かないことがある。環境変数は、今 の状態が引き継がれる。
% csh -f -x run![]()
-n オプションを付けて実行して、構文のチェックだけ行う。
シェルに1行ずつ与えて実行してみる。
---------------------------------------------------------------------- <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 にも反映される。 ----------------------------------------------------------------------
入出力の切替え
---------------------------------------------------------------------- <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 したい時など使う。 ----------------------------------------------------------------------
---------------------------------------------------------------------- #!/bin/sh ls_l() { ls -l $* } ls_l $* ----------------------------------------------------------------------
---------------------------------------------------------------------- % lsfile1 file2 file3 % foreach f (*)
foreach? ----------------------------------------------------------------------
---------------------------------------------------------------------- % sh* の代りに、$* を使えば引数、`cat file` を使えば、file の内容ついてループを実行することができる。$ for f in *
> do
> echo $f
> done
file1 file2 file3 $
----------------------------------------------------------------------
---------------------------------------------------------------------- % @ 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 %
----------------------------------------------------------------------
exit(0);
した時に、成功(true)、それ以外の時に失敗(false) と扱う。
csh では、$status でも調べられる。% if ( { /usr/bin/true } ) echo okok % echo $status
0 % if ( { /usr/bin/false } ) echo ok
% echo $status
255 %
![]()
強制的に縦に並べるオプション ls -1 や横に並べるオプション ls -C もある。---------------------------------------------------------------------- % lsfile1 file2 file3 % ls | cat
file1 file2 file3 %
----------------------------------------------------------------------
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)が使える。
-v オプションを付けると、マッチしなかった行が表示される。
-s オプションを付けると、パタンを含むか含まないかの判定だけを行い、画 面に結果を表示しない。シェル・スクリプトの if 文の中で使う。
「*」、「$」、「[]」など、シェルが展開してしまう文字をパタンとして指定 する時には、シングル・クォート「''」で括る。
egrep は、機能が拡張され、アルゴリズムも改良されていて速い。
~/bin
がpath シェル変数(PATH
環境変数)に含まれるようになっている。自分で作成したプログラムやスクリ
プトを、~/bin
に置くと./
などで実行する必要はない。
ただし、ファイルを作成し、chmod +x した後で、1度だけ rehash コマンド
を打つ必要がある。
chmod も rehash も、シェルごとに1度だけやればよい。kterm をたくさん開 いていた時には、それぞれのシェルでrehash コマンドを事項する。% mule ~/bin/newcommand% chmod +x ~/bin/newcommand
% rehash
% newcommand
% mule ~/bin/newcommand
% newcommand
% mule ~/bin/newcommand
% newcommand
%
![]()
rehash は、新しいシェルが実行される時には自動的に行われている。次にロ グインした時、chmod +x した後に開いた kterm ではrehash を実行する必要 はない。
---------------------------------------------------------------------- % ls-c ~/syspro-2001/file/ヒント:ls の結果から、egrep で .c や .h で終わるものを抜き出す。ある いは、シェルのパタン・マッチの機能を使って、*.c や *.h だけを取り出す。fd-print.c file-copy.c mmap-head.c stdio-thru.c utmp-print.c wtmp-last10.c ystat.c ystat.h ----------------------------------------------------------------------
余裕があれば、-l などのオプションが付けられるようにしなさい。
---------------------------------------------------------------------- % ls-dot ~ヒント:ls に -a オプションを付けると、全てのファイルを表示する。 先頭が「.」のものを抜き出す。. .. .cshrc .emacs .login %
----------------------------------------------------------------------
csh, tcsh, sh で 「.*」と指定して探す方法もある。
余裕があれば、-l などのオプションが付けられるようにしなさい。
---------------------------------------------------------------------- % ls-dir ~ヒント:ls -l で、d から始まる行だけを抜き出す。あるいは、csh の -d や test の -d でディレクトリだけを選びだす。Mail lib syspro tmp %
----------------------------------------------------------------------
できるだけ上のようにディレクトリの名前だけ表示するようにしなさい。 モードや日付は表示しないようにしなさい。
余裕があれば、-l などのオプションが付けられるようにしなさい。
---------------------------------------------------------------------- % ls-size ~yas/syspro-2001/cc/ヒント:ls -l の出力を sort コマンドでソートする。 ソートは、第5フィールド(-k5)で行う。-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 %
----------------------------------------------------------------------
---------------------------------------------------------------------- % 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 して、合計の行を削る。
---------------------------------------------------------------------- % ps -leそのうち、メモリのサイズ(SZ)が大きいプロセスを 10 個だけ表示するシェル・ スクリプトを作りなさい。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> %
----------------------------------------------------------------------
---------------------------------------------------------------------- % ps-sz-top10ヒント:SZ の順(第10フィールド)に sort して head する。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 %
----------------------------------------------------------------------
余裕があれば、RSS の順、CPU 時間の順に表示するスクリプトを作りなさい。
類似のことを実行するプログラムとして top がある。
% diff-backup *.cヒント:foreach または for 文で、引数のファイルについて、 "$file"~ のような名前のファイルが存在するかを調べる。 存在すれば、diff コマンドで表示する。%
![]()
余裕があれば、比較しているファイルの名前を表示したり、ファイルごとに停 止する、引数を取る、diff に対するオプションを取る、などの工夫をしなさ い。
% mv-lower [A-Z]*![]()
ヒント:ファイル名を echo して、tr で小文字にして、それを `` でシェル変数に入れる。元の名前から小文 字の名前に mv で変える。
余裕があれば、大文字と小文字を変換することで、ファイルが上書きされる時 には警告を出したり、ユーザに問い合わせたりするようにしなさい。
% find-pattern printf *.cヒント:egrep -s で調べて、含んでいた時だけファイル名を表示する。arg-print.c env-print.c signal-int.c %
![]()
% countdownヒント:foreach か for のリストに数を並べる。 sleep 1 で、1ごとに止める。5 4 3 2 1 0 %
![]()
余裕があれば、引数で何秒数えるかを指定できるようにしなさい。
% countdown 33 2 1 0 %
![]()
% wakeup 12:00ヒント:date コマンドの結果から何秒 sleep するればよいかを 計算する。計算には、csh の場合は、@ 、sh の場合には、expr を使う。^G^G^G %
![]()
音を鳴らすのは、画面に '^G' (Control+G) 、または、'\07' をecho すれば よい。'^G' は、mule では、'^Q^G' (C-q C-G)、つまり、Control+Q に続き Control+G と打てば入る。
この課題では、at コマンドを使ってはいけない。
% show-n-m 10 20 filenameヒント:head コマンドと tail コマンドを組み合わせて使う。 head も tail も、表示する行数を与えることができる。![]()
余裕があれば、-n オプションを付けなさい。これは、ファイルに行番号を振 るものである。nl コマンドを使うとよい。(cat -n が使えるシステムもある。)
% last-buddy user1 user2 user3ヒント:last の結果を egrep でより分ける。この時、egrep の引数には、シェ ル・スクリプトの引数として与えらた文字列を、'|'で繋いだものを与える。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 ... %
![]()