シェルの基本的な機能について理解し,活用しよう. シェルを使いこなせば, 複雑な操作や大量の操作を 楽々と実行できるようになる.
ユーザがほんの少し操作するだけで, コンピュータに多くの仕事を指示できる場合, 「操作効率が高い」という. シェルの威力を実感してみよう.
$ tar zxvf renum.tgz $ ls ... renum/ ... $ cd renum $ ls 1.txt 10.txt 2.txt 3.txt ... 9.txt
ファイルの表示順序がなんだか変なので, 次のように,ファイル名を修正したい:
$ ls 01.txt 02.txt 03.txt ... 09.txt 10.txt
どうすればよいか?
これは一般人でも,ファイルマネージャ (Wind◯ws の「エクスプローラ」や Mac◯S☓ の「ファインダ」等)で GUI 操作「名前の変更」を 何度もひたすら繰り返せばできますね? でも,進んでやりたがる人はいますか?
または,この授業に参加している学生ならば, 文字端末(Linux の「端末」や MacOSX の「ターミナル」等)で Unix コマンド「mv」を 何度もひたすら繰り返せばできますね? しかし,実行しないこと!!
$ mv 1.txt 01.txt $ mv 2.txt 02.txt $ mv 3.txt 03.txt ... $ mv 9.txt 09.txt
...こんな方法だと, 「ユーザがコンピュータに使われている」 ようなものだ.
まあ,この問題ではファイルは10個しかないから, 奴隷とまでは思えないかもしれないが, 1,000個とか100,000,000個とかだったらどうするんだ?
要するに,とても面倒だし時間もかかるので, もっと楽に素早く済ませるべきだ.
コンピュータにコマンド「mv」を繰り返させる. シェル「bash」の場合:
$ for i in ?.txt > do > mv $i 0$i > done
または,一行で:
$ for i in ?.txt ; do mv $i 0$i ; done
これなら, 「ユーザがコンピュータを使っている」 と言えるだろう.
コマンドラインが複雑になってはいるが, もしファイル数が増えたとしても, ユーザの手間はほとんど変わらない. 要するに,とても効率的.
なお,「for」や「?」等の意味については,後日説明予定. 今は気にしなくてよい.
OS とユーザとのインタフェース(仲介)として働くプログラムが シェル(shell)である. プログラムを実行したり, そのプログラムの入出力ファイルを指定したりするとき, ユーザからの命令は,まず,シェルによって解釈され, シェルが OS を制御する. ユーザが OS を直接に制御しているわけではないのだ.
Unix では,操作方法や機能の異なるさまざまな種類のシェルを利用できる. bash,tcsh,zsh,等が有名. 本科目では,bash(Bourne-again shell;ばっしゅ)を利用する.
最近のシェルは,特に操作効率を向上するために, 便利な機能をたくさんもっている. シェルの種類によって操作方法や機能は異なるが, ほとんどのシェルには,次のような機能がある:
まず,端末内で動作中のシェルが何であるか確認しよう:
$ ps $$ PID TTY STAT TIME COMMAND 3634 pts/0 Ss 0:00 -bash
もし,別のシェルだった場合,bash を起動:
$ bash # bash の起動
bash が起動されている場合, 特別なシェル変数 BASH および BASH_VERSION に, シェルのプログラムファイル名およびバージョン名が, それぞれセットされているハズだ. これらの変数の内容を表示してみよう:
$ echo $BASH # プログラムファイル名の確認 /bin/bash $ echo $BASH_VERSION # バージョン名の確認 4.1.2(1)-release # (異なっていても気にしないでよい)
ここで,「echo」は文字列を標準出力するための組み込みコマンドだ. また,「$」は変数を展開する(変数から内容を取り出す)ための演算子だ.
補完機能を試してみよう:
$ ech[Tab] $B[Tab]_V[Tab]O[Tab] # 長ったらしいコマンド echo $BASH_VERSION が少ない打鍵数で完成!
コマンド名,変数名の他,ファイル名も補完できる. 長いコマンドラインを入力するとき,この機能は非常に便利だ. 今後,最大限に活用しよう.
他のシェル変数も試してみよう. なお,シェルの変数には,C言語にあったような型の違いはない. シェル変数は,すべて文字列型である. ここでは,文字列の区切りやクォートの規則をおぼえよう.
まず,変数への文字列(単語)の代入:
$ tmp=hoge # 代入では,'=' の前後の空白は NG $ echo tmp = $tmp tmp = hoge
以下,同じようなコマンドの繰り返しなので, 上下カーソルキーなどで履歴を活用し, 能率的に入力しよう.
続いて,空白入りの文字列を代入してみよう:
$ tmp=hoge hoge # これは間違い -bash: hoge: command not found $ tmp="hoge hoge" # これが正解 $ echo tmp = $tmp tmp = hoge hoge $ tmp='hoge hoge' # これも正解 $ echo tmp = $tmp tmp = hoge hoge
次は,変数の展開方法のバリエーション:
$ echo tmp = $tmp # 変数を展開して表示 $ echo tmp = ${tmp} $ echo "tmp = $tmp" $ echo 'tmp = $tmp' # 変数を展開しない $ echo tmp = \$tmp
各自で実行結果を比較してみよう. 記号 $ { } " ' \ の意味がわかっただろうか?
特殊文字について,いろいろ試してみよう:
$ echo " # 終わらない → もう一個 " を入力 $ echo ' # 〃 → もう一個 ' を入力 $ echo \ # 〃 → もう一個 [Enter] を入力 $ echo \" $ echo \' $ echo \\ $ echo "\" $ echo '\' $ echo "'" $ echo '"' $ echo "\'" $ echo '\"' $ echo "\"" $ echo '\''
さらに深く,記号 " ' \ の意味がわかっただろうか?
既存のコマンドやコマンドの組み合わせに対して, 自由にエイリアス(別名)を定義できる. よく使う長いコマンドを短くエイリアスしておくと便利だ.
たとえば,以前の実験で, 次のような長ったらしいコマンドを何度も使っていた:
$ cc -Wall -lncurse -lm game.c $ cc -Wall -lncurse -lm game.c -o game
こんな場合には,次のようにエイリアス定義:
$ alias ccc='cc -Wall -lncurses -lm'
しておけば,ちょっと短いコマンドライン:
$ ccc game.c # cc -Wall -lncurses -lm game.c が実行される $ ccc game.c -o game # cc -Wall -lncurses -lm game.c -o game
...だけで済む訳だ.
既存のコマンドと同じ名前のエイリアスも定義できる. これは,あるコマンドでは,いつでも同じオプションを使いたい, というような場合に便利だ.
たとえば,ls コマンドの本来の標準動作では:
$ ls a.out source.c subdir
これだと結果が非常にわかりづらいので, 標準動作を次のように変更したい: (すでに設定されているかもしれない.)
$ alias ls='ls -F --color' $ ls # 本来の ls ではなく,エイリアス ls -F --color が実行される a.out* source.c subdir/ # わかりやすーい $ \ls # エイリアスの ls ではなく,本来の ls を実行 a.out source.c subdir # わかりづらっ $ /bin/ls # これでも本来の ls を実行 ...
エイリアスや変数の設定は,シェルを終了する(端末を閉じる)と消滅してしまうが, 毎度毎度,シェルを起動するたびに手動で設定するのは面倒だ. そこで,設定ファイル(初期化ファイル) ~/.bash_profile および ~/.bashrc を利用しよう. これらのファイルにコマンドを記述しておくと, シェル起動時に自動的に設定されることになる.
~/.bash_profile の内容:(ログインシェル起動時実行したい設定)source ~/.bashrc # ~/.bashrc を実行 または . ~/.bashrc # ~/.bashrc を実行
~/.bashrc の内容:(サブシェル起動時に実行したい設定)
alias ccc='cc -Wall -lncurses -lm' alias ls='ls -F --color' ...
これで,次回のシェル起動時から,これらのエイリアスが有効になる.
いちいち,シェルを起動しなおすのが面倒なときは, 次のようにすればよい:
$ source ~/.bashrc または $ . ~/.bashrc
すでに設定されているエイリアスを確認するには:
$ alias # すべてのエイリアスを確認 alias ccc=... alias ls=... ... $ alias ls # ls を確認 alias ls=...
エイリアスを解除するには:
$ unalias ls # エイリアス解除 $ alias ls # 確認 bash: alias: ls: not found
bash の関数はエイリアスの強化版であり, 引数付きのコマンドを定義できる. 例えば:
$ function backup > { > cp $1 $1.org > }
ここで $1 は最初の引数を意味する特殊な変数である.
$ echo "hello world" > hello.txt $ ls hello.txt $ cat hello.txt hello world $ backup hello.txt cp hello.txt hello.txt.org $ ls hello.txt hello.txt.org $ cat hello.txt.org hello world
なお,関数についても,必要に応じて, 設定ファイル ~/.bashrc に記述しておくとよい.
過去のコマンドを再度入力するには,上下カーソルキーの他, 次のような方法もある:
$ history | less # コマンド履歴をリスト表示 ... 3 echo $BASH_VERSION ... 25 tmp=hoge ... 50 history | less $ !! # 直前のコマンドへ置換 → history | less を実行 $ !3 # 3番目のコマンドへ置換 → echo $BASH_VERSION を実行 $ !-2 # 2つ前のコマンドへ置換 → 何でしょう? $ !e # 最近実行された e で始まるコマンドへ置換 → 何かな? $ mkdir totally-idiot-very-long-name-directory-you-should-not-mkdir $ cd !$ # 直前のコマンドの最後の引数へ置換→ totally-... を再利用 # 再利用せず,cd totally-... なんて再度打つのは鬱...
要するに, 履歴置換(!なんとか)によって, 過去のコマンドや引数を再利用できる. これらをうまく利用すれば, コマンドラインでのタイピング労力を大幅に節約できる. (キーを押す回数だけでなく,ミスタイプも削減できる.)
なお,上の例の最後に作ったディレクトリを消すには, 次のようなコマンドラインが必要になる: (まだ,実行しないこと!!)
$ cd .. $ rmdir totally-idiot-very-long-name-directory-you-should-not-mkdir
これを,できるだけ少ないタイプ数で実行してみよう.
まず,1行目(cd)については,文字数が少ないので直接入力が早い. 2行目(rmdir)については,次のように操作する方法がある:
もちろん,mkdir した直後であれば,次の方法が良い:
$ cd .. ; rmdir !$
以上のように,履歴や行編集の機能をうまく利用すれば, コマンドラインの入力は非常に楽になる.
新しいウィンドウを開くようなプログラムを実行したとき, コマンドラインが使えなくなって,困った経験はないだろうか? そんなときには...:
$ kturtle # 新規ウィンドウなアプリ(kturtle)をフォアグラウンドジョブ(表の処理)として実行 ls # この時,シェルはコマンド入力を受け付けていない. ^Z # [Ctrl]+[Z] → kturtle を中断 $ ls # コマンド入力の受け付けが再開されている. ... $ bg # 中断中の kturtle をバックグラウンドジョブ(裏の処理)として再開 [1] kturtle & # kturtle のジョブ番号が1になった. $ ls # コマンド入力が受け付けられている. ... $ gedit & # 別のアプリ(gedit)をバックグラウンドで実行 # もし gedit が無い場合,emacs など, # 新規ウィンドウを開くプログラムならなんでもOK [2] gedit & # ジョブ2になった. $ jobs # ジョブの一覧を表示 [1] + 実行中です kturtle [2] - 実行中です gedit $ kill %1 # ジョブ1を終了 $ kill %2 # ジョブ2を終了
シェルの組み込みコマンド,特殊変数,履歴,などの詳細は, オンラインマニュアルに記載されている:
$ man bash
Web で読みたければ... bash 日本語マニュアル -- JM Project
例:プログラミング作業で使う一連のコマンドの効率的な入力方法
$ mkdir MmDd # (本日の作業用ディレクトリを作れ) $ cd MmDd # 直前と同じ引数 $ vi Source.c # (何かバグ入りのCプログラムを作れ) $ cc Source.c # 直前と同じ引数 コンパイルエラー $ vi Source.c # 以前と同じコマンド $ cc Source.c -lncurses -Wall # 以前と同じコマンドへの追記 ... $ ./a.out
どんなエイリアスや関数があれば便利か? 実際に作成し試用しよう.
このウェブページに書かれていないが重要と思われる機能があるか?など