--/--/-- (--)
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
2010/12/20 (月)
UNIXとかLinuxの標準的なコマンドの中に、

「指定行を表示する」コマンド

ってないのだろうか。

catのオプションとかにあってもいいと思うんだが、自分が知らないだけだろうか。



いやまぁ、sedとかawkを使えばできるんだけどさ、、、

sed -ne '5p' hoge.txt

とか

awk 'NR==5' hoge.txt

とか。

あとは、

head -n 5 hoge.txt | tail -n 1

なんて方法もあるけど、これは頭が悪いと思う。

なんでないんだろう……
スポンサーサイト
2010/12/20 (月)
リダイレクトはなんとなくでは使いこなせない。

それを示す例としてよく見かけるのが、以下のコマンドである

command 2>&1 > file

やりたいのは「コマンドの出力とエラー出力の両方をfileに出力」なのだが、
上記のコマンドは「エラー出力を標準出力に出力、出力をfileに出力」という動作になる。
正しくは、

command > file 2>&1

である。
こんなのは序の口だが一応どうしてそうなるかを説明しておく。

まず、

command > file

は、

command 1> file

の省略形であることに注意。
ファイル記述子1の出力をfileにするという意味である。

なお、初期状態では
ファイル記述子0の入力元:標準入力
ファイル記述子1の出力先:標準出力
ファイル記述子2の出力先:標準エラー出力

となっている。

2>&1

とはファイル記述子1(出力用)をファイル記述子2に複製(dup(2)システムコール)するという意味である。

これをふまえて以下を考える。

command 2>&1 1> file

2>&1でファイル記述子1をファイル記述子2に複製する。
1の出力先は標準出力になっているので、2の出力先も標準出力になる。
1> fileで1の出力先はfileになる。
このとき2の出力先は標準出力のままである。

言葉で説明してもわかりずらい。
他サイトにいけばわかりやすい図とともに教えてくれると思う。

ただ、そんなわかりやすいサイト達も、厳密に教えているところは少なくて、
ここでわかってほしいのは、

> file



1> file

の省略形であるということ。それから、

2>&1

はファイル記述子2にファイル記述子1を複製するということ。
"複製"という言葉も大事だし、複製する方向も重要。置き換わるのは手前の2のほうである。
ちなみに

0<&3

みたいな入力の場合も、複製されて置き換わるのは0のほうである。

意味わかんない人は他サイトでdup(2)システムコールとリダイレクトの関係を理解したほうが良い。

で、以下の例。これがわかる人はもうリダイレクトは完璧(?)だろう。

while read line
do
echo $line
read input
echo $output
done < hoge.txt

何がやりたいのかというと、
「hoge.txtから1行ずつ読み取って出力し、その間で標準入力からも何か入力して出力したい」例である。
上記のままだと、hoge.txtとread inputの入力が標準入力でかち合ってうまく動作しない。
この解決には大きく分けて以下の3通りが考えられる。

解決法1
while read line 0<&3
do
echo $line
read input
echo $input
done 3< hoge.txt

解決法2
exec 3<&0
while read line
do
echo $line
read input 0<&3
echo $input
done 0< hoge.txt

解決法3
exec 3<&0
while read line 0<&4
do
echo $line
read input 0<&3
echo $input
done 4< input.txt

なぜこうすると解決するかは、各自で読み解いてほしい。
3つめの解決法が一番冗長だが、わかりやすいかもしれない(?)。
ちなみに、doneの後ろのリダイレクトが適用されるのはwhileからdone全てである。ここがミソ。

これができるかできないかで、きっと、シェルスクリプトの記述の幅が広がる。

ちなみに、cshみたいな>&で普通の出力とエラー出力の両方を取得できてしまうようなあまっちょろいシェルを使っていると、
上記のようなシェルスクリプトは一生書けない。
cshはファイル記述子の操作を行うことができないのだ。
/bin/shで厳密な処理に慣れてから、bashやzshに手を出すのが一番だと思う。
2010/12/19 (日)
ユーザのディレクトリは、

~名前

というチルダで始まるディレクトリを指定することで、絶対パスや相対パスを指定しなくとも移動できる。

ところで、他ユーザのディレクトリ群を自分のホームディレクトリとは別のディレクトリにマウントしていたりするとこの機能を利用できないことがある。

そんなときは自分で「名前付きディレクトリ(named directory)」に登録すれば良い。

そう、やっぱりzshならそんなことが可能なのだ。

hash -d hoge=/mnt/home/hoge

とコマンドを入力すれば

cd ~hoge

で/mnt/home/hoge

に移動できる。

for i in /mnt/home/*
do
hash -d $i=/mnt/home/$i
done

とかすれば、いっぺんに登録できる。
2010/12/19 (日)
バックスラッシュを入れるとかそんな話ではありません。

zshでコマンド履歴を利用していると、頻繁に改行を挿入したくなる。

というのも、複数行にわたるコマンドを履歴から呼び出すと、間に新しい行を入れたくなるからだ。

そんなときは

Esc Enter

または

Alt-Enter

で可能。

結構ググって調べたけどどこにも載ってくなくて、なんとなく試してたら発見した。

知ってから良く使ってる。
2010/12/19 (日)
シェルスクリプトというものは、分解すれば結局はそのほとんどがコマンドでできている。

例えば以下の例、

if [ $num = 0 ] ; then
echo "hoge"
fi

まぁ、典型的なif文の使用例である。

「もしも変数numの値が0だったらhogeを表示」みたいな。

これを厳密に書くと以下のようになる。

if test $num -eq 0
then
echo "hoge"
fi

if文の"if"の後ろには"条件式"を記述しているのではなく、
"コマンド"を記述していると意識したほうが良い。
コマンドの終了コードによって分岐するしないを判断している。

"[]"というのはあくまでtestコマンドの省略系である。
"$num = 0"とはtestコマンドの引数で$numと0の文字列比較を表している。

さらに、厳密に言えば"[ $num = 0 ]"というのも"["コマンドである。
試しに"which ["とかやってみると、/usr/binあたりに[コマンドが見つかる。
"]"という引数が[コマンドの引数の終わりを示している。
よく"["と"$num"の間には必ずスペースが必要なのは変な制約だとか言う人がいるけど、
それはコマンドとコマンドの引数の間にスペースが必要という、ごく当たり前の制約である。

"="と"-eq"の違いは文字列比較と数値比較の違いである。
どっちもtestコマンドオプションである。
例のように意図した条件分岐ができたとしても"="で数値比較するのはおかしい。
これらはman testとかやると詳細を知ることができる。
よく[ -f ~/hoge.txt ]"とか"[ -d ~/hoge ]"とか"ファイルの存在を調べるにはどうするんだっけとか聞かれるけど、
man testして調べれば一瞬で解決する話。

先ほど言ったように、if の後ろはコマンドの終了コードによって分岐するしないを判断する。
つまりifの後ろは必ずしも"[ ]"や、まして"test"でなくても良いとわかる。

if grep "hoge" hoge.txt

なんて記述も、わかってしまえば普通である。
grepコマンドは、該当の文字列が存在しなければエラーの終了コードを吐くので条件判断に使えるのだ。
ただし、grepは検索結果を標準出力に出すので、

if grep "hoge" hoge.txt > /dev/null

とするのが賢い。
また、先ほどの、ファイルの存在を調べるオプションは-fだったっけみたいなのも、もはや
if ls hoge.txt > dev/null
で解決するかもしれない。

"; then"というのは;はコマンドの改行、その後thenを入力しているに過ぎないのは、もうわかるだろう。



こうして考えると、

if [ $num = 1 ] && [ $num = 2 ]; then

の本当の意味もわかってくるだろう
&&というのは前のコマンドの終了コードがエラーでなければ次のコマンドを実行するという、選択実行である。
つまり"num = 1"がエラーの終了コードを吐けば$num = 2は評価されない。
ちなみに||は前のコマンドがエラーの終了コードを吐くと次のコマンドを実行するという選択実行、
;は前のコマンドの終了コードによらない連続実行である。

if [ $num = 1 -a $num = 2 ]; then

も分岐結果は同じになるが、(-aはandに相当するオプション)
後ろの式も評価される。

このへんを理解していると、シェルスクリプトの動作が格段にわかるようになる。

昔はzshの機能に頼って

for i in {0..5}

なんて記述をしていたけど、最近はなんとなく

for i in `seq 0 5`

という記述をするようになってしまった。
このほうが、厳密で好き。

どう展開されるかなんて、やってみないとわからないから
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。