最終更新日 … $Date: 2002-07-03 13:16:31+09 $
unix では短い単純なプログラムを上手に繋げて複雑な動作を素早く柔軟に行なう,
というのが基本姿勢の一つである.
そのための仕組みの一つが,標準入出力とその変更であるリダイレクション,
パイプラインである.
これらの概念は unix cui 環境での作業では必須であるので,あって当たり前という空気のように思えるぐらいにまで,きちんと学んでおこう.
unix ではプログラムはチャネル,もしくは デスクリプタ(ファイル記述子) とよばれる出入り口ファイルを用いてデータの入出力を行なう. 機器や環境に依存しない汎用入出力を可能とする目的で, unix ではこのうちの最初の 3つ(0 〜 2番)のデスクリプタを特別なものとして決めている. これらが標準入出力とよばれるものであり,次のような性質を持っている.
これら標準入出力によるデータの入出力の相手はプログラム側からは決められず,
そのプログラムを起動したシェルが決める約束になっている.
実は,シェル上で動かしたプログラムが表示しているものは,
その多くが,
プログラムが標準出力に出力 → シェルがディスプレイに繋げて出力
という形で出力されているのである.
標準入出力先を何にするかは実はシェルで簡単に変更できる.
これを上手に使えば,同じプログラムの出力をファイルへ出力したり,ディスプレイへ出力したり,
出力させなかったりと変更することを「プログラム側で全く考慮する事なく」
行なうことができる.
この「標準入出力先を変更すること」を(I/O)リダイレクションとよぶ.
これによって,簡単に作られたプログラムを非常に広い対象に適用することが
unix では可能になっており,「unix の cui 環境は使い込めば強力である」
といわれる一因ともなっている.
リダイレクションの方法はシェルによってわずかに異なるため,以下に
sh 系と csh 系とにわけて示しておく.
具体的には,下の表の左の項目をシェルでコマンドの最後にくっつければよいのである.
多すぎて良く分からないという場合は,
赤字
で書かれている3ヶ所を理解して覚えるだけでも十分だろう.
sh 系 | csh 系 | 意味 |
---|---|---|
< file
|
標準入力を file に変更.
|
|
<< endstr
|
標準入力を最初の endstr から次の endstr までの内容に変更.
"Here Document" 機能とよばれる. |
|
> file
|
標準出力を
file
に変更.
file
が存在しなければ作成され,既に存在するならば内容は(消去されて)上書きされてしまう.
csh 系および新しめの sh系で noclobber 変数をセットしてあるときは,既に file
が存在するならばエラーになる.
(大切なファイルに上書きして内容を失わないための機能) |
|
>| file
|
>! file
|
(noclobber 変数を無視して)
標準出力を
file
に変更.
noclobber 変数をセットしてあっても無視して強制的に上書きする. |
>> file
|
標準出力の出力を
file
の末尾に追加していく.
file
が存在しなければ作成される.
ただし,csh 系で noclobber 変数をセットしてあるときは, file
が存在しなければエラーになる.
|
|
>>! file
|
(noclobber 変数を無視して)
標準出力の出力を
file
の末尾に追加していく.
|
|
2> file
|
標準エラー出力を
file
に変更.
ファイルの存在, noclobber 変数との関係は上記同様. |
|
( コマンド > file1 ) >& file2
|
標準出力を
file1
に,
標準エラー出力を
file2
に各々変更.
(標準エラー出力だけを変更することはできない) ファイルの存在, noclobber 変数との関係は上記同様. |
|
> file 2>&1
|
>& file
|
標準出力と標準エラー出力の出力を両方とも
file
に変更.
ファイルの存在, noclobber 変数との関係は上記同様. |
>| file 2>&1
|
>&! file
|
(noclobber 変数を無視して)
標準出力と標準エラー出力の出力両方とも
file
に変更.
noclobber 変数をセットしてあっても無視して強制的に上書きする. |
>> file 2>&1
|
>>& file
|
標準出力と標準エラー出力の出力の両方を
file
の末尾に追加していく.
ファイルの存在, noclobber 変数との関係は上記同様. |
>>&! file
|
(noclobber 変数を無視して)
標準出力と標準エラー出力の出力両方とも
file
の末尾に追加していく.
|
例えば,
ls -lg
の結果をゆっくり調べたいとしよう.
画面に出力されるものを覚えておくのも手かも知れないが(^-^),
人間の記憶力には限界がある.
そこで,この出力結果をファイルにしておいて,じっくり眺めるのが良い.
それには,
ls -lg > file
とすれば,
file
という名前のファイルに
ls -lg
の結果が収められる.
だから,例えば less コマンドを使って
> less file 合計 9756 -rw-r--r-- 1 ot-026fd others 0 May 11 14:32 #.xemacs# -rw-r--r-- 1 ot-026fd others 36 May 11 14:34 #test.txt# drwx--s--x 23 ot-026fd others 2048 May 29 20:52 ./ drwxr-sr-x 43 root sys 1024 Apr 8 18:54 ../ -rw-r--r-- 1 ot-026fd others 0 May 11 08:47 .Change2tcsh -rw------- 1 ot-026fd others 505 May 28 16:14 .ICEauthority drwxr-sr-x 3 ot-026fd others 512 May 24 13:57 .Mathematica/ -rw-r--r-- 1 ot-026fd others 23158 Apr 15 22:56 .RealNetworks_RealMediaSDK_60 -rw-r--r-- 1 ot-026fd others 485 Apr 15 22:56 .RealNetworks_RealPlayer_60 -rw-r--r-- 1 ot-026fd others 92 Apr 15 22:56 .RealNetworks_RealShared_00 drwxr-sr-x 3 ot-026fd others 512 May 8 14:53 .Wnn6/ …略…と中身をいつでも繰り返しゆっくり見ることができる.
file
の中身の 5 番目の要素を大きい順に並べればよい.
この時は,入力と出力の両方をリダイレクションすれば便利だ.
だから以下のようにすればよい.
sort +4 -nr < file > file2
file2
に望みの結果が入っている.
/dev/null
上の表を見ても分かるように,リダイレクト先にはファイルを指定する.
この時,覚えておくと便利な特別なファイルが存在するので記しておく.
unix には,
/dev/null
という特別なファイル(デバイスファイルの一種)が存在する.
このファイルは「ブラックホールファイル」とでも言うべき次の性質を持っている.
よって,この性質を使って次のようなことができる.
cp /dev/null file
file
という名前の(空っぽの)ファイルが作られる.
touch file
とするという方法がよく知られている.
>& /dev/null
リダイレクションは標準入出力を自分の指定したファイルに変更できる.
よって,リダイレクションを使えば,
[ファイルを媒介したプログラムの接続] Program A →(出力)→ ファイル →(入力)→ Program B
とすることで二つ以上のプログラムの入出力をファイルを媒介することで繋げることができ,
単純な機能の組み合わせで複雑なことができる.
しかし,毎回ダミーのファイルを介在させるのは無駄だし,間違えも起きるだろう.
そこで,
Program A の標準出力と Program B の標準入力を直接繋ぐための簡単な方法が
unix では用意されている.
それは「パイプ」もしくは「パイプライン」と呼ばれ,次のように使う.
sh 系 | csh 系 | 意味 |
---|---|---|
Program A | Program B
|
Program A の標準出力を Program B の標準入力として渡す. | |
Program A 2>&1 | Program B
|
Program A |& Program B
|
Program A の標準出力と標準エラー出力を Program B の標準入力として渡す. |
(注) パイプは好きなだけ何段にも重ねることができる.
例えば,
ps -axu
の結果を画面で見るとしよう.
そのままこのコマンドを実行すると,
大量のデータが出力されて画面上を流れていってしまうのは経験しているだろう.
こういうときは,less コマンドなどを用いて,一画面ずつ表示させるのが定石である.
それには
ps -axu | less
とすればよい.
さらに,この結果をプロセスの名前順に並べ直すことによって見易くしたいな〜
と思ったとしよう.
すると,sort コマンドを使って,上の結果を並べ直せば良い.
それにはどうするかというと,パイプを二回使って,
ps -axu | sort +10 | less
とすれば,
sb8015th 8377 0.0 2.8 16700 3660 ? S 20:38 0:00 (dns helper) ot-026fd 8540 0.0 1.0 2384 1380 pts/0 S 21:04 0:00 -csh ot-026fd 8559 0.0 1.0 2384 1380 pts/0 R 21:19 0:00 -csh ot-026fd 8427 0.0 0.9 1900 1164 pts/0 S 20:48 0:00 -sh root 6796 0.4 12.2 41760 15644 ? S 19:03 0:35 /etc/X11/X -au root 1575 0.0 0.3 3180 444 ? S 14:21 0:00 /usr/bin/gdm - root 6797 0.0 1.2 3848 1652 ? S 19:03 0:00 /usr/bin/gdm - sb8015th 8267 0.0 3.0 6712 3848 ? S 20:38 0:00 /usr/bin/gnome sb8015th 8331 0.0 2.6 5304 3352 ? S 20:38 0:00 /usr/bin/sawfi sb8015th 8356 0.9 18.2 31716 23340 ? S 20:38 0:23 /usr/lib/netsc sb8015th 8305 0.0 2.4 5152 3128 ? S 20:38 0:00 /usr/local/bin …略…などと,プロセス名順に並んだ結果が画面上で一画面ずつ見られるというものだ.
tee
リダイレクションやパイプを使うときに,
「ファイルにも取っておきたいけど,
画面で確認しながら作業できないので不安だ」
という思いがあると思う.
こういう時便利なのが
tee
コマンドで,
「標準入力を,標準出力とファイルにコピーする」機能を持つ.
どう使うかというと,例えば先の
ls -lg
の結果をファイルに収めるという例を考えてみよう.
この作業を先のやり方
ls -lg > file
でやると,
file
の中身を見るまでどうなっているかわからない.
しかし,tee コマンドを用いて,
ls -lg | tee file
とすると,結果は画面にも出力されつつ,
file
という名前のファイルにも書込まれることになる.
慎重に作業を行ないたい,という時はこのコマンドを上手に使うのが良いだろう.
# 作業過程を記録しておく,という意味で似たようなものに
script
というコマンドがある.
どちらも使いこなせるようになって,適切に使い分けよう.
シェルには,コマンド置換という便利な機能がある.
これは,コマンド中の ` (バッククォート, 逆引用符) で囲んだ部分を
「先に実行してその結果で置き換える」
というものである.
これによって,コマンドの出力結果をあたかも文字列のように扱える.
簡単に言えば,通常ならば分けて行なう複数の操作を一行に書ける,ということである.
例えば,test-日付 という名前のファイルを作りたいな〜
と思ったとしよう(日付部分は月日の4桁とか).
日付を得るには,date コマンドが使える.
これはオプションを適当に指定すれば好きな形式で出力が得られる便利なコマンドだ.
例えば
date +%m%d
とすれば 4桁の数字の形で「月」「日」が得られる.
そこで,
touch test-`date +%m%d`
とする.
すると,コマンド置換によって,
赤字部分が先に実行されて,結果の4桁の数字に置き換えられる
ため,test-0529 などという(空っぽの)ファイルが作られることになる.
# これの応用で,「○○というプロセスを kill する簡単なコマンド」とかも作ることができる.
わざわざ ps -axu コマンドで調べて〜 とやらずに済むので楽だ.
少し考えてみよう.
# (ヒント) grep コマンド(文字列を検索する)を使うとよいだろう.
もうちょっと複雑な例も考えてみよう.
例えば,README という文字が頭についているファイルがいくつあるか調べたいな〜
と思ったとしよう.
すると,ls コマンドと wc コマンド(単語数を数える)を用いて
ls README* | wc -w
とすれば,該当するファイルが 3つあれば 3 という答えが得られる.
で,この作業を良く行なうので,このコマンドを alias などで簡単に入力できるようにしたいとしよう.
# alias についてはこの授業の後半部分で詳細を示す.
これは csh 系であれば,例えば
alias Rnum 'ls README* | wc -w'
と入力しておけばよい.
あとは
Rnum
と入力するだけで 3 とか 4 とか答えが得られる.
しかし,数字だけしか表示されないので味気ない.
そこで,echo コマンド(文字列を表示する)と組み合わせて,
alias Rnum 'echo "`ls README* | wc -w` Readme files exist."'
とする.
すると,
Rnum
と入力するたびに,
コマンド置換によって,
赤字部分が先に実行されて,結果の数字に置き換えられる
ため,
3 Readme files exist.などという結果が表示されるようになる.
unix は自由度の高さ,というのも基本姿勢の一つである.
そのため,あらゆる場面で個人が自ら設定できる余地が非常に大きい.
これは裏を返せば,適切な設定を意識して行なわないといつまでたっても
unix を便利につかえるようにならない,ということでもある.
そこで,この講義では unix cui 環境を支えるシェルについてその設定を学び,
個人設定の技術的な側面について学ぶとともに,
unix における個人設定の重要さや幅の広さを理解するのが狙いである.
sh 系 | csh 系 |
---|---|
|
|
種類 | シェル変数 | 環境変数 | ||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
概要 |
シェルが固有に持つ変数.
シェル自身の動作にかかわる設定に用いられる.
名前は小文字で記述することが多い. |
シェルから起動されるコマンドの動作設定にかかわる変数.
環境変数をどう利用するかはコマンドが決めることである.
名前は大文字で記述することが多い. |
||||||||||||||||||||||||||||||||||
文法 |
|
|
||||||||||||||||||||||||||||||||||
(設定する)
代表的なもの |
|
|
エイリアス(alias) とは,コマンドに「別名」をつける機能である. エイリアスの目的は,いくつかある. 具体例も示しつつ解説しよう. 例はなるべく bash 形式で書いてある.
alias md='mkdir'
alias em='xemacs'
alias newfile='touch dummy-`date +%m%d`'
alias printfile 'a2ps \!* | lpr'
alias cp='cp -i'
alias mv='mv -i'
alias rm='rm -i'
alias man='jman'
alias ls='ls --color=auto -Fga'
alias newpath='PATH=hogehoge'
エイリアスを用いるための文法は次のようになる.
エイリアス単独で考えると,引数を用いることができる
csh 系のシェルの方が圧倒的に便利である.
よって,簡単な作業でエイリアスを使って便利にするぞ,
というのであれば csh 系のシェルを使うのが良いだろう.
ただし,sh 系のシェルには「関数」という仕組みがあり,
これを使えば csh 系のエイリアスよりも強力なエイリアスを作ることができる,
と事実上言える.
まあ,そこまで機能をきちんと使うならば,
シェルスクリプトを組む手間とあまり変わらないので,
どのシェルでも十分に多くのことが可能であり,特にどのシェルがよい悪いという区別はないといえる.
# csh 系シェルでシェルスクリプトを組むべきではない,という流派もいるが(^-^).
シェルを使ってやや複雑なプログラムを組むこうした機能に関しては,
シェルスクリプトについて学ぶ講義で解説を行なう.
意味 | sh 系 | csh 系 | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
別名設定 |
alias 別名='コマンド'
|
alias 別名 'コマンド'
csh 系の場合,引数を次のようにして alias に含めることができる.
|
||||||||
解除 |
unalias 別名
|
|||||||||
表示 |
alias
|
上に出てきた TERM, TERMCAP といった環境変数やそれに類するものを設定する.
bash ならば .bash_profile ファイルで,
csh 系シェルならば .login ファイルで行なうのが良いだろう.
実際,通常ユーザが端末設定を行なう必要性はあまりないし,
トラブルことも多いので,
特に必要がなければ端末設定はしない方がよい.
# 昔は stty erase ^H などという設定をよく行なった.
しかし,現在のシェルではこうした設定はほとんど必要ないだろう.
ただし,キーバインディング(キー操作)を変えたいという場合は,
bash や tcsh のような新しめのシェルでは結構安全な方法が用意されている.
bash では .inputrc というファイル中にしかるべき設定を,
tcsh では bind コマンドを使って .tcshrc ファイル中でしかるべき設定を行なえばよい.
詳しくはオンラインマニュアルなどで調べること.
bash ならば .bash_profile で,
csh 系ならば .login で,ログイン時に起動されるコマンドを書いておく,
ということもできる.
これをうまく使って,例えば起動時に
などということができる.
この辺りは技術的なことよりも,運用の工夫に負うところが大である.
まあ,あまり凝らないほうがいいとは思うが(^-^).
といわれても良くわかんないよ… という泣き言が聞こえてきそうなので,
簡単なサンプルをつけておこう.
これを copy & paste するだけでもずいぶん便利に使えるようになるだろう.
この授業ではシェルのカスタマイズだけに絞って話を進めているが,
他にもエディタ自身の設定や,各種ソフトの設定はいくらでもある.
特に emacs の設定ではほとんど「なんでもできる」ほど自由度が高く,
真面目に取り組めば終りはない(^-^).
unix では,「ソフトは各人の好みに応じて設定して使うもの」
という思想が徹底しているので,積極的に自分に合う設定を探して設定を変更してみるのがよい.
シェルの環境をカスタマイズしているときに,
「設定にかなり厳しいミスをしてしまい,ログインできなくなる」
という現象が起こることがある.
いったんこうなってしまうと,通常ユーザではどうしようもなくなることがあるため,
こうならないようにするよう注意が必要だ.
では具体的にはどうすればよいかというと,
カスタマイズが終わるまで絶対にログアウトしない kterm を一つは確保しておく
とか,
何か設定を変えたら別の端末(ソフト)からログインしてテストする
(= 現在の端末(ソフト)はログアウトしない)
などとすればよい.