UNIX の核となるアイデアは、単純なコマンドをパイプやリダイレクトにより結 合して、本当に複雑な作業でさえ行なえるようにすることです。以下の例を御覧ください。 最も混みいった問題についてのみ説明しましょう。他のことについては、これまでの 章や man ページを参考にしてみてください。
問題 : ls
を使うとファイルの一覧がスクロールして、画面から消えてしまう。
解答 :
$ ls | less
問題 : 単語のリストを含んだファイルがあります。それを逆順でソートし、印刷したいのですが。
解答:
$ cat myfile.txt | sort -r | lpr
問題 : データファイル内に同じデータを含む行が幾つもあります。 それを切り詰める方法はありますか。
解答:
$ sort datafile.dat | uniq > newfile.dat
問題 : 「mypaper.txt」か「mypaper.tex」か、それに似た名前のファイ ルがあるのですが、どこにあるのか思い出せません。見つける方法は?
解答:
$ find ~ -name "mypaper*"
説明 : find
はディレクトリツリー(この例では、~
以降)の
全てのファイルを一覧するすごく便利なコマンドです。-name
を使いフィ
ルターをかけて出力することができます。
問題 : 「entropy」を含んだファイルがディレクトリ中にあります。
SEARCH
コマンドのようにそれがどのファイルにあるか探す方法はありますか?
解決方法:はい、このようにします。
$ grep -l 'entropy' *
問題 : どこかに「entropy」を含んだテキストファイルがあると思うんで
すが、それがどこのなんていうファイルか知りたい。VMS では search entropy
[...]*.*;*
を使いましたが、grep
ではサブディレクトリ以下を再
帰的に探すことができません。いい考えありますか?
解答:
$ find . -exec grep -l "entropy" {} \; 2> /dev/null
説明:find .
はカレントディレクトリから始めて、全てのファイル一覧を出力し、
-exec grep -l "entropy"
は各ファイルを対象に実行します
({}
により表される)。\
はコマンドを終了させます。
当然、この構文が面倒だと感じるでしょうね。
代わりに、次のようなスクリプトファイルを書くと良いでしょう。
(訳注:私は find . -type f |xargs grep 'entropy'
のようにしています)
#!/bin/sh # rgrep: 再帰的な grep if [ $# != 3 ] then echo "Usage: rgrep --switches 'pattern' 'directory'" exit 1 fi find $3 -name "*" -exec grep $1 $2 {} \; 2> /dev/null
説明: search
のような grep
と find
とを組み合わせれば、
両方の世界で最も使いやすいものになると思います。
問題 : 2 行のヘッダーで始まるデータファイルがあり、各行には不要なスペー スで区切られた n 個のデータがあります。各行の 2 番目と 5 番目のデータが欲 しいのですが、Fortran でプログラムを書いた方がいいでしょうか?
解答 : いいえ、こっちの方が早い。
$ awk 'NL > 2 {print $2, "\t", $5}' datafile.dat > newfile.dat
説明:コマンド awk
は実際はプログラム言語で、データファイルの 3 行目
から開始して、各行の 2 番目と 5 番目をタブで区切ってプリントします。
awk
を学びなさい --- 多くの時間を節約できますよ。
問題 : FTP サイトからダウンロードした ls-lR.gz
の内容を調べた
い。サブディレクトリ毎に「計 xxxx 」の行が含まれています。xxxx はディレ
クトリ内容を KB 単位のサイズで表したものです。この xxxx の集計を行ないたいのですが。
解答:
$ zcat ls-lR.gz | awk ' $1 == "total" { i += $2 } END {print i}'
説明:zcat
は .gz
ファイルの内容を出力し、awk
にパイプし
ます。awk
は man ページに丁寧にのっていますよ ;-)
問題:データファイルの値を計算する Fortran プログラム myprog
が
あります。数百のファイルを読み込ませて結果を出力したいのですが、データファ
イル名をいちいち打つのが面倒です。VMS では長いコマンドファイルを書くと
思いますが、Linux ではどうすればいいのでしょう?
解答: すごく短いスクリプトでできますよ。myprog
が常に「 mydata.dat
」を読み、結果を標準出力( stdout )に表示するようにしておいて、次のスクリプト
を書きます。
#!/bin/sh # myprog.sh: 多くの異なるファイルに対して同じコマンドを実行します。 # 使用方法: myprog.sh *.dat for file in $* # for all parameters (e.g. *.dat) do # ファイル名を result.dat に追加していきます。 echo -n "${file}: " >> results.dat # 現在の引数を mydata.dat にコピーして、myprog を実行します。 # そして、出力を results.dat に追加します。 cp ${file} mydata.dat ; myprog >> results.dat done
問題 : 私のテキストファイル内の「geology」を全て「geophysics」に置 き換えたいのですが、手作業でしなければならないのでしょうか?
解答:いいえ、このシェルスクリプトを書いてください。
#!/bin/sh # $* の $1 を $2 に置き換えます。 # 使用方法:replace "old-pattern" "new-pattern" file [file...] OLD=$1 # スクリプトの最初のパラメータ NEW=$2 # 2 番目のパラメータ shift ; shift # 最初の二つのパラメータを捨てる。次はファイル名です。 for file in $* # パラメータとして与えられた全てのファイルでループします。 do # OLD を NEW に置換して、テンポラリファイルに保存します。 sed "s/$OLD/$NEW/g" ${file} > ${file}.new # テンポラリファイルをオリジナルファイル名にリネームします。 /bin/mv ${file}.new ${file} done
問題 : 幾つかデータファイルがあって、その長さは判らないんですが、 最後から 1 つ前の行と 2 つ前の行を削除するには 、えーと...手作業ですか?
解答: もちろん、ノー。スクリプトを書いてください。
#!/bin/sh # prune.sh は n-1番目と n-2 番目の行をファイルから削除します。 # 使用方法: prune.sh file [file...] for file in $* # 全てのパラメータでループします。 do LINES=`wc -l $file | awk '{print $1}'` # ファイルの行番号 LINES=`expr $LINES - 3` # LINES = LINES - 3 head -n $LINES $file > $file.new # 最初 KINES 行を出力します。 tail -n 1 $file >> $file.new # 最終行を加えます。 done
これらの例があなたの興味をそそりますように...