UNIX 講習会 (4) 演習問題

目次へ

Last modified: Tue Jun 19 14:57:45 2001

4. シェルの使い方 (2)

締め切りは 6/20。

提出先:

問題は、おもにシェルスクリプトを作ることです。 以下の 19問から何問か選んで解答すること。各問題の後に [1] とか [2] という数字がついていますが、これはその 問題の点数です。 各自、合計が最低でも 5点以上になるように 解答してください。 解答はメールに添付してもらえればいいです。質問は随時受けつけます。

おしらせ (6/17 追加)

Bourne Shell の文法や内部コマンドについて調べたいときは sh の man ページを見てください。ただし、これは機種によって違いがあります。

問題:


注意


問題

あるスクリプトを n 回実行 [1]
repeat_n 10 ls /etc などとやると、 ls /etc を 10回くり返して実行するような シェルスクリプトを書け。コマンドには任意個の引数が 指定可能になっていること。

ディレクトリ名にスペースが入ることは想定しなくてよい。

引数がまちがっている場合はちゃんと ヘルプを標準エラー出力に表示して、異常終了するようにすること。

(ヒント: 任意個の引数をどうやってサポートするか? シェル内部コマンドの shift を参照)


ファイルのパーミッションを 644 に [1]
指定されたディレクトリ内にあるすべてのファイルのパーミッションを 644 にし、同時にすべてのディレクトリのパーミッションは 755 にするスクリプト chmod_644 をつくれ。

ディレクトリは複数指定可能で、何も指定されなかったときは カレントディレクトリ以下を対象とする。 find と xargs を使うこと。

(ヒント: 3行で終わる)


ログのローテート [1]
ログファイルが出力されているディレクトリを、1日 1回空にしたいが、 万が一のため、古いログの内容は別の(日付がわかるような) 名前にしてとっておきたい。 しかしディスク容量には制限があるので、指定した日時より前の ログを自動的に消すようにしたい。これを行うシェルスクリプトを書け。

rotate_log ログ用ディレクトリ 古いログを残す個数 のように指定したい。 たとえば rotate_log /var/log/syslog 3 とやると /var/log/syslog が空になるが、古い /var/log/syslog の内容は /var/log/syslog-20010529 などに残される。そして 3日以上前の 「/var/log/syslog-なんとか」というディレクトリは消されるようにしたい。

ディレクトリ名にスペースが入ることは想定しなくてよい。

引数がまちがっている場合はちゃんと ヘルプを標準エラー出力に表示して、異常終了するようにすること。

(ヒント: basename, dirname を使おう)


ファイル名一括変換 [1]
複数のファイル名に含まれているパターンを一括変換したい。 rename str1 str2 file1 file2 ... filen で、指定された各ファイル名に含まれている文字列 str1 を 文字列 str2 に置換するようにせよ。

ディレクトリ名にスペースが入ることは想定しなくてよい。

引数がまちがっている場合はちゃんと ヘルプを標準エラー出力に表示して、異常終了するようにすること。


メニュー操作 [1]
コマンドラインからユーザの入力を読み、 次のような動作を行うシェルスクリプト smallmenu を作れ。

(ヒント: シェル内部コマンドの read を参照)


長時間アクセスしていないファイル・ディレクトリ削除 [1]
指定された日数だけアクセスされていないファイルおよび ディレクトリを すべて削除するようなスクリプト remove_noaxes_files を作れ。 スクリプトには削除の対象となるディレクトリ (複数指定可) と、 何日アクセスされていなければ削除するかを指定する数値を 与えるものとする。つまり、

remove_noaxes_files 3 ~/work ~/tmp

などとやると、~/work と ~/tmp 以下で 3日以上アクセス (読み書き) されていないファイルとディレクトリをすべて削除する。 ほかのコマンドと違って、 ディレクトリがひとつも指定されなかった場合は危険なのでエラー 終了させること。

引数がまちがっている場合はちゃんと ヘルプを標準エラー出力に表示して、異常終了するようにすること。

また、このスクリプトには設計上の手落ちがある。 たとえばファイルを削除でなく特定のフォルダに移動させたい場合は スクリプトを書き換えねばならない。このスクリプトを より一般的に利用できるようにするには、どのように設計を変更すれば よいか議論せよ。

(ヒント: find)


ファイルの行数によって cat と less を使い分ける [1]
ファイル名がひとつ指定されると、その行数を調べ、 それが現在の端末に表示しきれる場合は cat、そうでない場合は less を使って表示するスクリプト pager を作れ。

ファイルが複数指定された場合はつねに less を使い、 ファイルの指定がなかった場合には標準入力から入力された内容を less でそのまま表示するようにせよ。 現在の端末に表示できる最大行数は stty size を実行した値を使う。

(ヒント: wc)


自分が読めないディレクトリを捜す [2]
find_unreadable /tmp などとやると、 /tmp 以下で自分が読めない (自分の所有で所有者パーミッションが 開いていないか、他人の所有で他人あるいはグループへのアクセスが 許されていない、など) ディレクトリをすべて標準出力に 表示するようにしたい。ディレクトリ名が省略された場合は カレントディレクトリ (.) を使うこと。

(ヒント: find を使うが、自分が読めない条件を全部指定するのは大変だよね)


ファイル名の拡張子を判断して印刷 [2]
ファイル名の拡張子を判断して自動的に異ったタイプの ファイルを印刷するスクリプト lprx をつくれ。 ファイル名が .txt あるいは .c で終わっているファイルは a2ps を通してテキストで印刷し、 .ps で終わっているファイルは PostScript ファイルとして そのまま印刷。また .dvi で終わっているファイルは dvips を通して PostScript ファイルに変換し印刷するようにせよ。 これ以外の拡張子のファイルが与えられた場合はエラーを表示せよ。 なお、このさい環境変数 PRINTER で指定されたプリンタを使うものとする。

ファイル名が指定されない場合は、標準入力からテキストファイルが 入力されたものとして扱うこと。

(ヒント: ファイルの拡張子を判定するには case を使います)


平均得点算出 (awk 使用) [2]
東工大の院試では、学部生が内薦 (はいはい、今は「口述試験」というんでしたね) を受けられるかどうかは 学部時代の科目の平均点にかかっている。 しかしこの平均はすべての科目の平均ではなく、 良い点数をとった上位 n 科目が選ばれる。

以下のように「科目名 点数」の形式のテキストファイル:

       代数系と符号理論	         60
       プログラミング第三        99
       コンパイラ構成            82
       オペレーティングシステム  95
       数理計画法                55
       ...
を与え、整数 n を与えると、 上位 n 科目の平均点を出力するようなシェルスクリプト calc_avg を書け。 たとえば

% calc_avg 30 mytranscript.txt のようにすると、上位 30位までの点数の平均が表示される。

引数がまちがっている場合はちゃんと ヘルプを標準エラー出力に表示して、異常終了するようにすること。

(ヒント: awk)


Zipf の法則の検証 [2]
ある文章に出てくる単語の出現回数 (頻度) m は、 その頻度の順位 n と反比例の関係にある。つまり m × n = 一定。 この法則が日本語の文章と英語の文章について成りたつかどうか、 それぞれ手頃なシェルスクリプトを使って確認したい。 次の 2つのものを作れ。

どちらのスクリプトも1つのファイル名を引数として与え、 各行ごとに単語名と出現回数およびその順位を出力するようにせよ。 なお、英文における単語の区切りは空白あるいはカンマ、ピリオドで よい。日本語文の場合は形態素解析器として juman と chasen の どちらを使ってもよい (snow には両方入っている)。1形態素 = 1単語と みなすこと。例文はどこかから適当に取ってくること。

引数がまちがっている場合はちゃんと ヘルプを標準エラー出力に表示して、異常終了するようにすること。

(ヒント: 英語なら tr | sort | uniq -c | sort -rn | cat -n)


クロスバリデーション [2]

自然言語処理では、少ないコーパスを使って十分な精度の オープンテストを行うために「クロスバリデーション (cross validation)」 という手法を用いることがある。クロスバリデーションとは、 たとえばほぼ同量のコーパス c1, c2, c3, c4 を持っていたとすると 以下のようにしてテストを行うものである。

  1. コーパス c2, c3, c4 をつかってパーザなどを訓練し、c1 で実際の テストをおこなう。
  2. コーパス c1, c3, c4 をつかってパーザなどを訓練し、c2 で実際の テストをおこなう。
  3. コーパス c1, c2, c4 をつかってパーザなどを訓練し、c3 で実際の テストをおこなう。
  4. コーパス c1, c2, c3 をつかってパーザなどを訓練し、c4 で実際の テストをおこなう。
  5. 各テスト結果を平均して、全体の結果を算出する。
この方法を使うためには、利用するコーパスをとっかえひっかえして パーザなどのプログラムを走らせる必要があるが、コーパスが 何十個とある場合、これを人手でやるのはかなり大変である。 これをある程度自動的におこなうシェルスクリプト cross_validate を つくれ。

% cross_validate train run data1 data2 ... datan

のようにすると、

  1. まずデータ data1 をテストするために訓練する。 データ data2, ..., datan を引数にして、 与えられたコマンド train を実行する。 このコマンドの標準出力は data1.train というファイルに 残される。つぎに、このファイルを引数にして 与えられたコマンド run が実行され、この標準出力が data1.result というファイルに残される。
  2. つぎにデータ data2 をテストするために訓練する。 データ data1, data3, ..., datan を引数にして、 与えられたコマンド train を実行する。 このコマンドの標準出力は data2.train というファイルに 残される。つぎに、このファイルを引数にして 与えられたコマンド run が実行され、この標準出力が data2.result というファイルに残される。
  3. ...

この実行が終わったときは、data1.train, data1.result, data2.train, data2.result ... というファイルができているはずである。 また、このスクリプトをより使いやすく、より一般的に利用できるように するためにはどんな点を改良する必要があるか議論せよ。

引数がまちがっている場合はちゃんと ヘルプを標準エラー出力に表示して、異常終了するようにすること。


空シンボリックリンク削除 [2]
与えられたディレクトリに含まれる空の (行き先ファイルが存在していない) シンボリックリンクを すべて削除するようなスクリプト remove_empty_links を作れ。 スクリプトには削除の対象となるディレクトリ (複数指定が可能) を 引数として与えるものとする。 引数が省略された場合はカレントディレクトリ (.) を使うこと。

また、このスクリプトには設計上の手落ちがある。 より一般的に利用できるようにするには、どのように設計を変更すれば よいかを議論せよ。

(ヒント: find)


ファイル容量超過警告メール [2]
1日 1回実行され、ディスク容量をチェックして もしディスクの残り容量がある割合以下だったら警告メールを 出すスクリプト check_diskfree を作れ。

df コマンドは、たとえば % df /home/tanaka などと やると /home/tanaka が含まれているディスクの残り容量を 表示する。これを利用して、

% check_diskfree /home/tanaka 5 euske@cl.cs.titech.ac.jp

などと実行したときに /home/tanaka の残り容量が 5% 以下だったら、 euske@cl.cs.titech.ac.jp に警告メールを送信するように したい。メールの内容には、どこのディレクトリの、いつの時点での 残り容量であるか、およびその割合を含めること。

引数がまちがっている場合はちゃんと ヘルプを標準エラー出力に表示して、異常終了するようにすること。

(ヒント: df の出力を切り出すのは awk を使う、 メールを送信するには /var/qmail/bin/qmail-inject を使うこと)


連番入りファイル名の付けかえ [2]
たとえば、 data001.txt, data002.txt, ... data999.txt というファイルがあったときに、 data001.txt 〜 data397.txt までのファイルだけに対して ファイル名中の「data」を「text」に変更したいとする。

このような指定された範囲の番号 (連番) がついたファイル名だけを 一括変換するようなシェルスクリプト rename_num を作れ。 たとえば、

% rename_num 001 397 data text .txt のようにすると、data001.txt 〜 data397.txt を それぞれ text001.txt 〜 text397.txt に変換するようにしたい。 ファイル名の一括変換スクリプト remane を作った人は、 これを呼び出すようにしてもよい。

引数がまちがっている場合はちゃんと ヘルプを標準エラー出力に表示して、異常終了するようにすること。

この呼び出し形式は非常に美しくない (一般性がない) ことに 気づくと思う。より一般的な方法で番号つきのファイル名を 操作するには、コマンドのインターフェイスをどのように設計すべきか 議論せよ。

(ヒント: printf というコマンドがある)


mv 実現 [2]
mv コマンドを使わずに、mv と同様のファイル名変更および移動を 行うスクリプト altmv をつくれ。ファイルを同一ファイルシステム上に 移動させる場合、実行時間はつねに一定であるようにすること (つまり、その場合に cp を使ってはいけない)。

ただし、mv と違って扱うのはファイルのみで、 ディレクトリやシンボリックリンクを移動あるいは名称変更する場合は 考えなくてよい。

引数が足りなかったり、ディレクトリを指定された場合は 標準エラー出力にエラーと正しい使い方を表示し、異常終了すること。

(ヒント: ln のマニュアルをよく読んで「ハードリンク」について 学習しましょう)


改行コード変換 [2]
テキストファイルは、OS によって改行コードの形式が異なる。 与えられたテキストファイルの1行目に含まれている 改行コードを判定し、それを自動的に UNIX 形式の テキストファイル (改行コード 0x0A) に変換するような スクリプト convtext を書け。

ファイル名は複数指定可能。 指定されない場合は、標準入力からテキストファイルが 入力されたものとして扱うこと。

(ヒント: tr, sed)


GNU でないgrepで前後数行を出力 [2]
通常の grep コマンドでは、grep はマッチした行しか 表示しない。これをマッチした行を含む前後 n行を表示するような スクリプト grepx を書け。書式は、

% grepx n file1 file2 ... filen のようにすること。n には前後何行を表示するかの行数を与える。 たとえば n に 1 を与えた場合、このスクリプトはマッチした行を その前後 1行とともに表示する。 (もちろん、通常の grep と同じように、マッチする行が複数存在した 場合はすべてにおいてこうならなければならない)

ファイル名は複数指定可能。 指定されない場合は、標準入力からテキストファイルが 入力されたものとして扱うこと。

(ヒント: cat -n、sed)


平均得点算出 (awk 不使用) [3]
先に述べた成績表の平均得点算出スクリプトを awk を使わずにシェルのみで書け。そのうえで、 シェルを使ったほうがよい場合と別のプログラミング言語を 使ったほうがよい場合について議論せよ。

^ toc   新山 祐介 euske@cl.cs.titech.ac.jp