ファイル操作

Ippei Kishida

Last-modified:2016/04/26 17:50:31.

1 ファイルの種類を判別(file)

テキストファイルだったら一個ずつ開いて人間が判別することもできますが、数が多くなると大変ですし、そもそもバイナリだったらお手あげです。 file コマンドを使うとファイルの種類を判別してくれます。 私は改行文字を識別するのによく使います。

windows から転送したファイルは大抵改行文字が CRLF になっています。 VASP の入力ファイルなんかでは UNIX で使われる改行文字 LF に変換してやる必要があります。

2 空のファイルを作る(touch)

空のファイルを作るには touch コマンドが便利です。 touch コマンドは本来、ファイルの更新時刻を変更するためのものですが、存在しないファイルを指定すると空ファイルを生成します。

3 1,2行のテキストファイルを作りたいとき

システム管理してたら「1行くらいのファイルを作りたい」ということがあります。 「vi some.txt」としても良いですが、そういう作業が何度もあると「vi を起動するのもたるいなあ」と思うことがあります。 そういう時は、 cat - > some.txt cat はファイルの中身をそのまま加工せずに標準出力に流すフィルタなんですが、引数「-」を指定した場合はファイルではなく標準入力の内容を流します。 入力を終了するには [C-d] を叩きます。

3.1 zsh の場合

zsh の場合はコマンドなしで > some.txt で行けます。 リダイレクトの左辺は通常コマンドからの標準出力ですが、左辺がない場合は標準入力から受けとるようになっています。 改行はそのまま入力し、入力を終了したい場合は C-d を入力します。

同様に「>> some.txt」もできます。 これはファイルの末尾に文字列を追加します

4 通信

5 telnet

kill コマンドは、プロセスを殺すためのコマンドではなく、本当はシグナルを送るためのコマンドでした。

デフォルトがあたかもそのコマンドの全てであるかのように思われがちなコマンドは他にもあります。 例えば telnet がそうです。 ですが、やっぱり普通の人には telnet は「リモートの計算機を操作するためのコマンド」と思っておけば十分です ……ということで以下は普通でない人向け。

telnet は厳密には「リモートの計算機とポートを指定して通信する」為のコマンドです。 ただそのデフォルトが telnet ポート(23番)を叩くというだけです。

例えば、以下の手順で HTTP 通信でデータを取得できます。

ippei@odin % telnet cow 80
Trying 192.168.101.14...
Connected to nas.adachi.lab.
Escape character is '^]'.
GET
<!DOCTYPE html
    PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
<html lang="ja">
......

cow の 80番ポート(HTTP)を telnet で叩いて、HTTP で定義されたコマンド「GET」で以てウェブページのストリームを取得しています。

利便性を他所に置けば、基本的に telnet があったら何でもできます。 メールクライアントのない環境で pop サーバに接続してメールチェックするということも出来ます。 個々のネットワーク系アプリケーションは、そのポート操作でよく使われるコマンドをまとめたり簡略化したりするためのインターフェイスであると言えるでしょう。

6 ftp と .netrc

.netrc に記述しておけば、リモートホストへの ftp ログインを一定自動化することができます。 ~/.netrc(パーミッションは600)に以下のように記述しておくと、「ftp moon1」するだけでログイン名、パスワードの入力が省略できます。

machine  moon1
login    ippei
password PASSWD

さらに、続けて以下の指定をすれば、ログイン後に binary だとか prompt だとか打っていた作業を自動化できます。

macdef   init
epsv4    off
binary
hash
prompt
(ここに必ず空行が必要)

ここで「macdef init」は「ログイン後に以下の init マクロを実行せよ」という指示、「epsv4 off」は「FTPパッシブモードをオフにする」という指示です。

「今時 ftp を使う場面もないだろう」? まあ研究室内ではそうでしょうけど、プロバイダのウェブサーバ相手に通信する時には FTP 以外なかったりするので私はそういう用途に使ってます。

rsh や ssh と同様、パスフレーズなしのログインをできるようにしておくと、スクリプトや cron で「ファイルのやりとりを自動化」することができるようになります。

6.1 ftp での送信を自動化する

echo 'put hoge.txt' | ftp remotehost

~/.netrc で指定してない場合は以下の方法も使えます。 (ps コマンドで「コマンドライン、すなわちパスワード丸見え」というセキュリティ的な問題がありますが) echo ‘put hoge.txt’ | ftp ftp://ippei:PASSWD@remotehost/home/ippei/

7 ssh / rsh

他のホストにログインする際に telnet を使っている人が多いと思いますが、 rsh は telnet みたいなもんです。 「rsh moon1」としてパスワードを入力するとログインできます。 さらに rsh は「rsh moon1 ‘ls /home’」のように第2引数としてコマンドを含めることができます。

パスフレーズ入力を省略できるようにしておけば、結構便利です。

7.1 「ssh でコマンドを投げる」のをバックグラウンドで実行する

ssh moon1

と打つと moon1 にリモートログインできます。 さらに ssh moon1 ls とすると moon1 で ls を実行することができます。 ということは、計算ファイルを転送しておいて ssh moon1 ‘cd calc1; vasp’ とすれば vasp の計算を moon1 上で実行することができる、ということです。

vasp のような「長い時間のかかるコマンド」はバックグラウンドで走らせたいところでしょう。 バックグラウンドで計算させたい場合はコマンドラインの最後に「&」を付ければ良い、ということで ssh moon1 ‘cd calc1; vasp &’ あるいは ssh moon1 ‘cd calc1; vasp’ & としてみたらどうでしょうか?

しかしこれは、どちらも上手く行きません。 * 前者の場合、リモートホストでバックグラウンドで処理しても、コマンドが返るのはバックグラウンドジョブが終了してからなので、制御が返るのは結局最後まで待たされます。 * 後者の場合、ジョブがサスペンド(中断状態)になって動かなくなります。

後者の状態については、 ssh が「標準入力がオープンされてなければ処理を進めない」ようになっているからのようです。 これは、「cat - &」したのと同様の状態です。 「cat -」 は標準入力から受け取ったストリームをそのまま標準出力に流しますが、バックグラウンドジョブでは標準入力が開いていないのでそのままサスペンドになってしまいます。 ssh がこのような仕様になっているのはおそらく、ssh では通常パスフレーズの入力を求められるからでしょう。

さて、ということで解答編です。 ssh に -f オプション(コマンドを実行する直前に、バックグラウンドに移行する)をつけて実行すれば、望みどおりにバックグラウンドで走らせることができます。 ssh -f moon1 ‘cd calc1; vasp’

#他の計算機へのファイルの転送 ##ftp ftp の致命的な欠点は、ディレクトリをそのまま転送できないことです。 ftp でディレクトリを転送したければ、事前に tar で固めるなどの処置が必要になります。

7.2 rcp / scp

rsh / ssh での通信が可能ならばこちらの方が便利です。 使い方は、 rcp -r foo moon1: rcp -r moon1:bar ./ 前者はローカルのfooディレクトリを moon1 に転送、 後者はリモートのbar ディレクトリを ローカルに転送します。 -r オプションはディレクトリの転送を意味します。 cp コマンドと同じですね。 なお、rsh / ssh ではデフォルトではローカルとリモートのアカウント名が一致することが想定されています。 リモートで違うアカウント(例えば program)として転送したければ、「program@moon1:」のように指定します。

7.3 rsync

rsync はコマンドというよりアプリケーションなので、どのUNIX にも入っているわけではありません (moon には入っていますが、goli や duo には入ってません)。 rsync は rsh / ssh の通信経路を使って、双方の差分だけ転送します。 ですから、バックアップを取るというような、「大半は一緒」というディレクトリを転送するのに適しています。 rsync -avz –delete foo moon1: 別の計算機にバックアップを取る用途にも使えますし、 転送元/転送先のいずれにもローカルホストを指定できるので自分自身でバックアップを取る用途にも使えます。

7.4 その他

nfs マウントが確立してるならば、これが一番ラクな場合もあります。

他、追い詰められた時の手段として、 リムーバブルメディア HDD換装してマウント *apache サーバになってるなら http を経由

8 管理用

9 su

su はスーパーユーザになるためのコマンドですが、su の後にユーザ名を続けて入力すると(例:su ippei)スーパーユーザではなく ippei になれます。 もちろん普通は ippei のパスワードが必要ですが、スーパーユーザから su ippei するとパスワードなしで ippei になれます。

例えばあなたがユーザ ippei のプロセスを殺す必要がある場合、「su」で一旦スーパーユーザになったあと、「su ippei」で一般ユーザ ippei になって ippei のプロセスを殺すと比較的安全に作業ができます。

スーパーユーザで kill するときに、プロセス ID をタイプミスするとエライことになることがあります。 可能な限りスーパーユーザ権限での作業は控えるべきです。

10 fsck

使わないで済めばそれに越したことはないのですが……。 残念ながら、計算機が正常に終了しないことはよくあることです。 ハードディスクに情報が完全に書き出されない内に電源が切れてしまうとファイルの中身と名前の対応が整合しない場合があります。 Linux などでは起動時にファイルシステムをチェックして不整合があるようだと勝手に修正してくれる筈ですが、他の UNIXではそうとは限りません。 あるいは、修正するためには然るべきオプションを付けて手動で実行する必要があるかもしれません。

UNIX の伝統的なファイルシステムをチェックするコマンドは fsck です(恐らく、「File System ChecK」の略でしょう)。 一見壊れたかに見えるハードディスクも、fsck をかければ元に戻る、あるいは使えるようになる「かもしれません」 (duo の外付け HDD もこれで直ったことがあります)。

11 処理の優先度を変更する(nice コマンド)

自慢のスクリプト machinestatus.rb ですが、スレッド化して改良することで桁違いのパフォーマンスを得ることができました。 しかし、反面 CPU 負荷が高くなりすぎてしまいました。 まあ当然の話です。 作業量は同じまま時間だけ短縮したのですから。

odin では machinestatus.rb を15分毎に走らせて情報を公開しています。 15分に一度手元の計算機が凍る(処理能力が一杯一杯になる)のはちょっとやってられません。 これに対する回答の一つが「処理の優先度を下げる」ことです。 こっちとしては 15分に一度走ってくれれば、それが10秒かかろうが100秒かかろうが大差ないわけです。 machinestatus.rb にはちょっと手を抜いて貰って、余った処理能力で手元の作業を進めるようにしてもらうんですね。

さて、「処理の優先度を下げる」コマンドが「nice」コマンドです。 「nice machinestatus.rb all」のように使います(all は machinestatus.rb の引数)。

オプションで「優先度の下げ幅」や「優先度を上げる(スーパーユーザのみ)」こともできますが、その辺は「man nice」を参考にして下さい。

あと、既に走っているプロセスの優先度を変更するには「renice」コマンドが使えるようです。 私は使ったことがありませんが。

12 shutdown

shutdown -h now

もしくは shutdown -r now ある程度管理をしたことのある人は、もう呪文として覚えてしまっていると思います。 この「now」の部分に実際にシャットダウンを行う時刻を指定することができます。 例えば「shutdown -h +30」とかすれば30分後にシャットダウンしてくれますし、 「shutdown -h 0503312359」とかすれば 2005年3月31日23時59分に シャットダウンしてくれます (OS によって若干書式が違うかもしれません)。

……ですが、大学の場合は停電時にコンセントを抜きに来なければいけないので あまり使えませんね。

shutdown コマンドの実行パーミッションは OS によって若干異なります。 RedHat Linux では、 -rwxr-xr-x 1 root root 14952 Mar 15 2002 /sbin/shutdown* となっており、全てのユーザが実行することができます。 が、実はコマンド内部で UID のチェックを行っており、 結局 root でしか実行できないようです。

FreeBSD では、 -r-sr-x— 1 root operator 168756 1 22 00:46 /sbin/shutdown* となっており、root もしくは operator グループの人間のみが実行できます。

個人的には後者のシステムの方が美しいと思うのですが、 皆がそれなりのスキルをもって皆で管理するという形態 (それなりの信頼関係が保たれているシステム)ならば 前者のままでも楽で良いと思います。

13 その他

14 bc -l

柔軟な書式で対話的に計算してくれる計算機です。 数式をコピペして ENTER してやると値が出ます。

計算目的ではワンライナーを使うよりこっちの方がはるかに便利です。

15 perl のワンライナー(一行スクリプト)で計算

xcalc や Windows の電卓を使っても良いのですが、これらを使う時はタイプミスに非常に気を使います。 テキスト上に数式がある場合に、それをコピペするだけで計算できる方法です。 コマンドラインで、 perl -e ‘printf(“%f?n”,( EVALUATION ));’ 上式の “EVALUATION” の部分に適当な式を入れて入力すると答えが返ります。 例えば、こんな。

perl -e 'printf("%f?n",(-488.715448 + 3*(-495.778551)) - (4*(-498.637996))*127/128);'
2.918446

累乗の演算子は ’**’。 また、sin(), cos(), exp(), log() といった関数も使えます。

ワンライナーの真価はもっと別に発揮できるらしいのですが、私はそこまで使いこなせていません。

16 date

計算機の時計時刻を知るには date コマンドを使います。 コンピュータの時刻は結構狂ってることが多いので、「タイムスタンプが変だな」とか感じたらチェックしてみて下さい。

また、計算機の時計を修正する際にも使います(スーパーユーザ権限で実行する。詳しくは man date を参照)。

スクリプトに組み込んで、以下のようにログを取るのにも使えます。

#!/bin/sh
echo 'start' > log
date >> log
vasp
echo 'end' >> log
date >> log

17 cal

cal コマンドでカレンダーをチェックできます。

また、2000 年の3月24日が何曜日だったかを調べたい時には、 cal 2004 cal 03 2004 などが使えます。

:グレゴリオ歴:基本的に cal コマンドはグレゴリオ歴で計算します。1582年10月4日までがユリウス歴、その翌日である1582年10月15日(10日省略)からグレゴリオ歴が始まりますので、これ以前の暦は ずれてしまいます。ユリウス歴での表示をしたい場合は然るべきオプションを付けなければなりません。

岸田は以前、普通のカレンダーを持たず cal コマンドだけで生活していたことがあるのですが、研究室に来てからその日が祝日であることに気付いた、ということがあります。

18 make のしくみ

アプリケーションプログラムをインストールしたりコンパイルするときによく使う make ですが、これは本来ソースを開発するために作られた物であって、試行錯誤しながらインストールする為の物ではない、ということに注意する必要があります。

make はソースファイルとそれをコンパイルして生成されるオブジェクトファイルの更新時刻のみをチェックし、ソースファイルよりオブジェクトファイルが新しい場合はコンパイルを実行せずに既存のオブジェクトファイルを使用します。 すなわち、コンパイルオプションを変更した場合は生成されるオブジェクトファイルは変化する筈なのですが、ソースファイルが変更されていないので、オブジェクトファイルはそのまま使用することになってしまいます。 当然これは大変マズいことです。

大抵の Makefile には make のターゲットとして clean (オブジェクトファイルを全て削除)が用意されているので、コンパイルオプションを変更しながら試行錯誤的にインストールを試す場合などは毎回「make clean; make」した方が無難です。

あ、そうそう。 zsh 使っている人は Makefile のあるディレクトリで make の後に[TAB]を叩いてみて下さい。 そのディレクトリにある Makefile の内容に応じた(!)補完リストが出て来る筈です。

18.1 love ターゲット

え、毎度馬鹿馬鹿しい。 今日は一つ、旧くなった馬鹿話を。

make は Makefile を読み込んで、そこに記述されたターゲットに対する処理を行います。 従って、Makefileがなければ make は動作しません。 しかし、一つだけ Makefile がなくても動くターゲットがあります。 いえ、「ありました」。 それは love ターゲットです。

ippei@odin % make love
Not war.

ただ、それだけ。

:Make love, not war:60年代ヒッピーのスローガンの一つ。「殺し合うのではなく、愛し合おう」

今ではこの JOKE もソース上にあるだけで、OLD_JOKE フラグを立ててやらないとバイナリに反映しないのでした。 なお、今はこうなります。

ippei@odin % make love
make: don't know how to make love. Stop

これはこれでアレですね。 Linux では、

ippei@moon1 % make love
make: *** No rule to make target `love'.  Stop.

なるほど、ルール無用のデスマッチですか。 色んな形がありますからね。

……この辺どうも、ターゲットに love が来た時にも自然な言葉になるようにエラーメッセージを作っているような気がします。

19 ファイルを検索する

19.1 find

非常にキメ細かな制御のできる検索コマンドです。 以下くどくど書いてますけど、覚えることはこれだけ、「find というコマンドがある」。 これ以上の情報は必要な時に「man find」すれば全て書いてます。

例えば「カレントディレクトリ以下のディレクトリで Makefile という名前のファイル(とディレクトリ)を調べる」時には find . -name Makefile ワイルドカードも使えますが、この場合はシェルに展開させずに find に伝えなければならないのでクォートかエスケープしてやる必要があります。 find . -name ’Makefile’ find . -name Makefile* -name の他に色々な条件を付加できます。 例えば、以下のようなものがあります。 -mtime 修正時刻と現在時刻との差 * -user ファイルの所有者 また、画面に表示するのではなく、別の処理に回すこともできます。 find ~/.trash ! -mtime -7 -maxdepth 1 -exec /bin/rm -rf {} では、条件にマッチしたファイルを「-exec」で rm コマンドに送っています。 (→マッチしたファイルを削除する)

find はキメ細かな検索ができるのですが、ファイルシステムを真っ向から走査するので結構時間がかかります。 ファイル検索の大半は「ファイル名による検索」なので、この用途にはもっと高速な検索コマンドがあります。

19.2 locate

ファイル情報に関するデータベースを前もって作成しておくことで高速化したファイル検索システムです。 データベース更新は週1度とかの頻度で cron から実行されます。

locate で検索できるのはファイル名だけなんですが、大変高速です。 キチンと比較したことないので分かりませんが、1001000倍くらい速いんじゃないでしょうか。 (odin(ディスク使用 25GB)でlocate をかけると 1秒以内に検索が終了します。 find は数分数十分くらいかかります。)

ただ、データベース更新してから移動したファイルなどは locate では追えません。 手動でデータベースを更新するか、find を使って下さい。

イマドキの PC-UNIX には大抵デフォルトで入ってるものなんですが、以前扱ったことのある RedHatLinux7.3 には入ってませんでした。 GentooLinux2006.0 では slocate パッケージを入れる必要がありました。

20 sleep

「暫くの間、5分毎に計算の進捗を見たい」という場合は以下のようにします。 ls /OUTCAR ; sleep 300 ; ls /OUTCAR ; sleep 300 ; …. sleep で 300秒間シェルを眠らせています。

21 yes

yes コマンドはひたすら「y」を繰り返すコマンドです。 「y/n」を尋ねてくるプログラム(例えば「mv -i」みたいな)で「全て『y』と答えるぜ!」という時には、 yes | mv -i foo/* bar みたいに使えます。

というても mv の場合は -f すれば良いのですが。 yes コマンドはストリームの仕組みを理解するのに役立つとは思うのですが、どう使って良いのかはイマイチ分かりません。

22 echo

22.1 セットされている環境変数の表示

環境変数を確認するには echo コマンドを使います. % echo $USER ↓ ippei 一見 echo コマンドが環境変数 $USER を解釈しているように見えますが, 実際はそうではありません. echo コマンドは受け取った文字列をそのまま返しているだけです.

ここでユーザとコマンドの間にはシェルが介在していることを思い出して下さい. そう, $USER の解釈を行っているのはシェルなのです.

    ユーザ     →     シェル      →    コマンド

「echo $USER」 → 「echo ippei」
                        ↓
                  echo コマンドに → echo コマンドが
                  「ippei」を渡す → 「ippei」を出力
  1. ユーザから「echo \(USER」というコマンドラインを受け取ったシェルは, 「\)」から始まる文字列を変数名としてそこに展開します. 私の場合は $USER が ippei に展開されるわけですね.
  2. そこで「echo ippei」に展開されたコマンドラインの第1要素である「echo」コマンドに, 「ippei」という文字列が渡されると,
  3. echo コマンドはそのまま「ippei」と出力するわけです.

同様のことはワイルドカードの展開にもあてはまります. echo report.* ただし, この用途には ls を用いた方が出力が見易いと思います. (また, zsh ユーザならば「C-x g」が便利です)

なお厳密には, 現行の多くのシェルでは echo はシェルの内部コマンドとして扱われます. すなわち echo コマンドが呼ばれた場合,外部コマンド echo に渡すのではなく 展開された文字列をシェルがそのまま出力します.

    ユーザ     →     シェル      →    コマンド

「echo $USER」 → 「echo ippei」
                        ↓
                      シェルが
                  「ippei」を出力

22.2 ユーザに情報を提示

プログラミング言語における print 文と同様, ユーザに情報を提示するのによく利用されます.

#! /bin/sh
echo "If you want to abort, type C-c."
echo "If continue, type C-d"
cat -                      #「cat -」は標準入力を待つ
echo "continue..."

上記のスクリプトでは「cat -」で入力待ちになるので, そこで C-c で中断するか, C-d で入力終了して続行するかを操作できます. その直前に「どのような操作ができるのか」を提示しておかなければ ユーザは戸惑ってしまいます.

22.3 スクリプト中でどこまで進んだかを表示する

「ユーザに情報を提示」の一種なのですが, これもよく用いられます. VASP の計算を連続して投げるシェルスクリプトを走らせる場合を考えます.

#! /bin/sh
cd ~/calc1
/usr/local/calc/vasp/vasp469serial > stdout
cd ~/calc2
/usr/local/calc/vasp/vasp469serial > stdout
cd ~/calc3
/usr/local/calc/vasp/vasp469serial > stdout
cd ~/calc4
/usr/local/calc/vasp/vasp469serial > stdout

このスクリプトを走らせてしまうと, 終了するまで計算がどこまで進んでいるか分かりません. そこで echo を挿入します. #! /bin/sh cd ~/calc1 echo “calc1” /usr/local/calc/vasp/vasp469serial > stdout

cd ~/calc2
echo "calc2"
/usr/local/calc/vasp/vasp469serial > stdout

cd ~/calc3
echo "calc3"
/usr/local/calc/vasp/vasp469serial > stdout

cd ~/calc4
echo "calc4"
/usr/local/calc/vasp/vasp469serial > stdout

変数も使えるので for 文の進捗も出力できます.

#! /usr/local/bin/zsh
for i in ~/calc*
do
  cd $i
  echo $i
  /usr/local/calc/vasp/vasp469serial > stdout
done

22.4 コマンドに入力を与える

パイプを通してコマンドに入力を与えることができるので, キーボード入力を先行入力しておくことができます. 例えば, make 中に一度だけ「yes」と答えなければならないことが分かっている場合, echo “yes” | make という手が使えます. (この用途には yes コマンドを使って「yes ‘yes’| make」 という手段もありますが)

この用途に関しては, 以前『UNIX今日の技/コマンド/ftp での送信を自動化する』の項で echo ‘put hoge.txt’ | ftp remotehost echo ‘put hoge.txt’ | ftp ftp://ippei:PASSWD@remotehost/home/ippei/ という技を紹介したことがありました.