UNIXシステムはすべてをファイルとして扱います。コマンドターミナルでさえデバイスファイルと関連付けられており、そのファイルに書き込むことでターミナルに情報を表示させることができます。例えば:
# 現在のターミナルに出力する
echo hoho > /dev/tty
# bash環境で、ttys001ポートに接続されているターミナルに送信する
echo hoho > /dev/ttys001
dd
指定したサイズのファイルを生成するために使用され、ハードディスクの読み書き速度の測定によく使われます。
// テストファイル test.data を生成する
// \0 で埋め尽くし、サイズは 10M とする
dd if=/dev/zero of=test.data bs=1024 count=10k
// 書き込み速度の測定
// ディスクに連続書き込みを行い、メモリバッファを使用せず、毎回 8k のデータを書き込み、計 4k 回実行して 32M のファイルを生成する
dd bs=8k count=4k if=/dev/zero of=test.data conv=fdatasync
// 読み込み速度の測定
dd if=test.data of=/dev/null bs=4k
if は入力ファイル(input file)、of は出力ファイル、bs は書き込みブロックのサイズを表します。/dev/zero は無限の \0 文字を生成する特殊なデバイス、/dev/null は入力をすべて捨てる空のデバイスです。
P.S. Mac の dd では conv 引数に fdatasync や fsync オプションはありません。
comm
comm コマンドはテキストファイルを比較して差分を表示します。ただし、入力ファイルはソートされている必要があるため、通常は sort と組み合わせて使用します:
# sortコマンドの -o オプションはファイルへの出力を指定。ここでは元のファイルを直接上書きする
sort a.txt -o a.txt; sort b.txt -o b.txt
# diffを実行
comm a.txt b.txt
P.S. wget の -O- オプションは面白い形をしており、ファイルとして取得して結果を STDOUT に出力することを意味します。
結果は以下の3列で得られます:
# aにありbにないもの bにありaにないもの 両方にあるもの
a - b b - a a ∩ b
この3列があれば、元のファイル a と b を復元できます(ソート済みのもので、ソート前には戻せません)。例えば a = (a - b) ∪ (a ∩ b) です。
-1/-2/-3 オプションで指定した列を削除できます。オプションは入力ファイルの前に記述する必要があります:
# 第3列を削除し、共通部分(a ∩ b)を出力しない
comm -3 a.txt b.txt
# abの差異を1列にまとめ、差異のある行のみを出力する
comm -3 a.txt b.txt | sed $'s/\t//g'
特に注意:正規表現の前の $ は値の展開を意味します。これを付けないとタブ文字にマッチしません。IFS=$'\n' と同じ理屈です。
P.S. Mac の sed -i によるインプレース置換は非常に面倒で、バックアップファイル名を指定する必要があります(空文字列でも可)。
diff
差異ファイルを生成するために使用されます:
# -u オプションで一般的なフォーマットを出力し、diff.txt に書き込む
diff -u a.txt b.txt > ab.diff
行単位でファイルを比較し、どの行が追加・削除されたかを示します(変更は削除と追加の組み合わせとして扱われます)。結果は以下のようになります:
--- a.txt 2017-03-15 10:50:34.000000000 +0800
+++ b.txt 2017-03-19 16:56:14.000000000 +0800
@@ -1,6 +1,11 @@
+
+end yaya
data
-end
is
-line
no
+line
this
+
+newend
+line
diff の結果を使用してパッチを当てることで、ファイルを復元・変換できます:
# aにパッチを当てる
patch -p1 a.txt < ab.diff
# abの内容が同じになる
md5 a.txt; md5 b.txt
元の内容に戻すには、もう一度 patch を実行します:
# パッチを戻す
patch -p1 a.txt < ab.diff
ディレクトリに対しても diff を行えます:
# -N は存在しないファイルを空ファイルとして扱う、-a はすべてのファイルをテキストとして扱う、-r はディレクトリを再帰的に比較する
diff -Naur data files
mkdir
ディレクトリを作成するために使用されます。既に存在する場合はエラーを返します:
mkdir: bak: File exists
通常はチェックが必要です:
# 存在しない場合のみ作成する
if [ ! -e path ]; then mkdir $path; fi
深いパスの場合、各階層をチェックするのは面倒なので、エラーを捨てることもできます:
mkdir ./dir1 2>/dev/null
mkdir ./dir1/dir2 2>/dev/null
mkdir ./dir1/dir2/dir3 2>/dev/null
より簡単な方法は:
mkdir -p ./dir1/dir2/dir3
既に存在する場合は無視し、必要なディレクトリのみを作成します。
ファイル権限
一般的な3つのカテゴリ:
-
user:ファイルの所有者
-
group:ユーザーグループ
-
others:所有者とグループ以外のユーザー
ls -l で表示される権限の形式は:ファイルタイプ(1文字) 所有者権限(3文字) グループ権限(3文字) その他権限(3文字) です。
ファイルタイプは以下の通りです:
- 通常ファイル
d ディレクトリ
c キャラクタデバイス
b ブロックデバイス
l シンボリックリンク
s ソケット
p パイプ
後ろの権限は、各カテゴリにつき4つの値(-rwx)があり、それぞれ なし/読み取り/書き込み/実行 を表します。
P.S. 権限が ---------- の場合、root 以外のすべてのユーザーはそのファイルに対して一切の操作(読み書き実行)ができません。
また、x の位置に表示される3つの特殊権限があります:
-
setuid:ユーザーが所有者の権限でファイルを実行できる。例:
-rws------ -
setgid:ユーザーが所有グループの権限でファイルを実行できる。例:
----rws--- -
sticky bit:スティッキービット。そのディレクトリを作成したユーザーのみが配下のファイルを削除できる。他のユーザーは書き込み権限があっても削除できない。例:
-------rwt
注意:s や t には大文字と小文字があり、小文字は x 権限があることを、大文字は x 権限がないことを表します。
権限の変更
chmod コマンドを使用します。例えば:
# 権限を設定する
chmod u=rwx g=rwx o=rwx test.sh
# 所有者に実行権���を追加する
chmod u+x test.sh
# 全員に実行権限を追加する
chmod a+x test.sh
# 所有者の実行権限を削除する
chmod u-x test.sh
数値を使って設定することもできます:
# u=rwx g=rwx o=rwx と同等
chmod 777 test.sh
777 は rwx の2進数表現を10進数にした3つの組です。例えば r-- は 4(100)です。
P.S. 一般にこれは8進数と呼ばれますが(0から7の値)、実際には2進数として解釈する方が合理的です。
3つの特殊権限の設定も chmod で行います:
# setuid, setgid, sticky bit
chmod u+s
chmod g+s
chmod o+t
数値で設定する場合、3つの権限の前に sst の組を追加します。例えば chmod 2777 test.sh の特殊権限は 2(010)で、これは setgid 権限を意味します。
所有権を変更するコマンドは chown です:
# 所有者を user1 に、グループを staff に設定する
chown user1.staff test.sh
通常、setuid 権限と組み合わせて使用されます:
# rootグループのrootユーザーにする
chown root.root bomb.sh
# 所有者実行権限を付与し、実行時にroot権限になるようにする
chown u+s bomb.sh
touch
touch コマンドは、ファイルが存在すればタイムスタンプを更新し、存在しなければ空のファイルを作成します:
# 存在すれば、すべてのタイムスタンプを現在時刻に更新する
touch test.sh
# 存在すれば、アクセス時刻のみ更新する
touch -a test.sh
# 存在すれば、修正時刻のみ更新する
touch -m test.sh
head/tail
ファイル内容の一部のみを表示します:
# 最初の10行を表示
head test.sh
# 最初の3行を表示
head -n 3 test.sh
# 最後の10行を除外して表示
head -n -10 test.sh
# 最後の10行を表示
tail test.sh
# 最後の3行を表示
tail -n 3 test.sh
# 最初の90行を除外して表示(91行目から最後まで出力)
seq 100 | tail -n +91
P.S. Mac では -n 引数に負の数を指定できず、head: illegal line count -- -10 というエラーになります。
ls でディレクトリのみを列挙する
3つの方法があります:
# -d オプションが最も簡潔
ls -d */
# -F でタイプ識別子を付け、/ で終わるものを抽出
ls -F | grep "/$"
# -l の結果(権限の先頭文字がタイプ)から d で始まるものを抽出
ls -l | grep "^d" | awk '{print $9"/"}'
find を使うこともできます:
# ファイルタイプを指定して検索
find . -type d -maxdepth 1 -mindepth 1 -print
1階層下のみを探すよう、depth の範囲に注意してください。
パスの切り替え
cd - (cd $OLDPWD) で直前のディレクトリに戻ることはよくありますが、さらに強力なものがあります:
# cd と同じように動作するように見える
pushd /tmp
# 履歴スタックを確認 (-v で番号表示)
dirs -v
# 直前のディレクトリに戻る
pushd
# その前のディレクトリに戻る
pushd +1
# さらにその前の...
pushd +2
pushd は作業ディレクトリを切り替え、スタックの先頭と指定した記録を入れ替えますが、スタックの長さは変わりません。記録を削除して戻る場合は popd を使います:
# 直前のディレクトリに戻り、現在のパスを削除する
popd
# 指定した履歴パスを削除する
popd +1
# 履歴スタックをクリアする(現在のパスのみ残す)
dirs -c
+N、-N は方向を表し、+N はスタックの先頭から 0123... と数え、-N はスタックの底から数えます。
P.S. oh my zsh の特定のバージョンでは +- の方向が逆になっていることがありますが、bash は正常です。
wc
行数、単語数、文字数をカウントします。簡単なコード統計によく使われます:
# 行数、単語数、文字数を出力
wc test.sh
# 行数のみ取得
wc -l test.sh | awk '{print $1}'
# 単語数のみ取得
wc -w test.sh | awk '{print $1}'
# 文字数のみ取得
wc -c test.sh | awk '{print $1}'
P.S. 単語カウント機能は弱く、スペースで区切られた文字列を1つの単語として扱い、句読点の区別もされません。
コメントはまだありません