シェル芸力向上のために moreutils
に含まれるコマンドを一通り試してみました。
$ rpm -qi moreutils
Name : moreutils
Version : 0.49
Release : 2.el7
Architecture: x86_64
Install Date: Fri 29 May 2015 06:34:50 PM JST
Group : Applications/System
Size : 155340
License : GPLv2
Signature : RSA/SHA256, Tue 04 Mar 2014 12:35:02 PM JST, Key ID 6a2faea2352c64e5
Source RPM : moreutils-0.49-2.el7.src.rpm
Build Date : Wed 26 Feb 2014 07:58:44 PM JST
Build Host : buildvm-21.phx2.fedoraproject.org
Relocations : (not relocatable)
Packager : Fedora Project
Vendor : Fedora Project
URL : http://kitenet.net/~joey/code/moreutils/
Summary : Additional unix utilities
Description :
This is a growing collection of the unix tools that nobody thought
to write thirty years ago.
So far, it includes the following utilities:
- isutf8: check if a file or standard input is utf-8
- sponge: soak up standard input and write to a file
- ts: timestamp standard input
- vidir: edit a directory in your text editor
- vipe: insert a text editor into a pipe
- combine: combine the lines in two files using boolean operations
- ifdata: get network interface info without parsing ifconfig output
- pee: tee standard input to pipes
- zrun: automatically uncompress arguments to command
- mispipe: pipe two commands, returning the exit status of the first
- lckdo: execute a program with a lock held
- ifne: run a program if the standard input is not empty
- parallel: run multiple jobs at once (contained in moreutils-p
$ rpm -ql moreutils | fgrep /usr/bin/
/usr/bin/chronic
/usr/bin/combine
/usr/bin/errno
/usr/bin/ifdata
/usr/bin/ifne
/usr/bin/isutf8
/usr/bin/lckdo
/usr/bin/mispipe
/usr/bin/pee
/usr/bin/sponge
/usr/bin/ts
/usr/bin/vidir
/usr/bin/vipe
/usr/bin/zrun
CentOS 7 で試したので、CentOS 7 の moreutils
に含まれるものだけです。
chronic
指定したコマンドを実行し、コマンドが非ゼロで終了するか、もしくは異常終了したときだけ、コマンドの標準出力と標準エラーを表示します。
# 終了コードがゼロなので標準出力や標準エラーにはなにも出力されない
$ chronic sh -c 'echo out; echo err 1>&2; exit 0'
# 終了コードが非ゼロなので標準出力や標準エラーに出力される
$ chronic sh -c 'echo out; echo err 1>&2; exit 1'
out
err
# 異常終了なので標準出力や標準エラーに出力される
$ chronic sh -c 'echo out; echo err 1>&2; kill $$'
out
err
例えば crontab で次のように使用します。
0 1 * * * chronic /path/to/command
コマンドが失敗したときだけ、標準出力と標準エラーの内容がメールで通知されます。
なお、出力は「標準出力 → 標準エラー」の順番になります。ので、次のように順番が入れ替わることがあります。
$ chronic sh -c 'echo 1st 1>&2; echo 2nd; exit 1'
2nd
1st
このコマンドは CentOS 6 の moreutils
には無いようです。
combine
ブール演算を使用して2つのファイルから行のセットを組み合わせる。
$ cat <<EOS> 1.txt
a
b
EOS
$ cat <<EOS> 2.txt
b
c
EOS
# 1.txt と 2.txt の両方に含まれる行
$ combine 1.txt and 2.txt
b
# 1.txt に含まれていて 2.txt に含まれていない行
$ combine 1.txt not 2.txt
a
# 1.txt と 2.txt の両方の行
$ combine 1.txt or 2.txt
a
b
b
c
# 1.txt のみの行、または 2.txt のみの行
$ combine 1.txt xor 2.txt
a
c
ファイルの内容をソートする必要はなく、2番目のファイルが1番目のファイルの行の順番に揃うようにソートされます(or の場合は2番目のファイルはそのままの並び順で1番目のファイルの後に続きます)。
errno
エラーコードやエラーのマクロ名からエラーの概要を検索します。
$ errno 2
ENOENT 2 No such file or directory
$ errno ENOENT
ENOENT 2 No such file or directory
-ls
や --list
で一覧表示できます。
$ errno -ls
EPERM 1 Operation not permitted
ENOENT 2 No such file or directory
:
EHWPOISON 133 Memory page has hardware error
ENOTSUP 95 Operation not supported
-s
や --search
で概要の文字列を検索できます。
$ errno -s "allocate"
ENOMEM 12 Cannot allocate memory
-S
や --search-all-locales
ですべての言語から検索できます。
$ errno -S "確保できません"
ENOMEM 12 メモリを確保できません
このコマンドは CentOS 6 の moreutils
には無いようです。
ifdata
I/F の情報を取得する。ifconfig
や ip
とは異なり、シェルで扱いやすい簡単な形式で出力する。
例えば IPv4 アドレスなら次のように取得できます。
$ ifdata -pa ens32
192.0.2.123
他にも下記のようなものが取得できます。
# I/F の存在チェック
$ ifdata -e ens32; echo $?
# IPアドレス、サブネット、ブロードキャストアドレス、MTU などを表示
$ ifdata -p ens32
# I/F の存在を yes/no で返す
$ ifdata -pe ens32
# IPv4 アドレス
$ ifdata -pa ens32
# サブネットマスク
$ ifdata -pn ens32
# ネットワークアドレス
$ ifdata -pN ens32
# ブロードキャストアドレス
$ ifdata -pb ens32
# MTU
$ ifdata -pm ens32
# MAC アドレス(Linux のみ)
$ ifdata -ph ens32
他にも統計情報を表示したり、実行時の1秒間のトラフィックを表示したりできます。
詳細は man ifdata
。
ifne
標準入力が空では無かったときに、指定されたコマンドを実行します。
# 標準入力があるのでコマンドが実行される(標準入力はコマンドに渡される)
$ echo -n hoge | ifne wall
Broadcast message from ore@ore-no-server (Tue Jan 12 22:22:30 2016):
hoge
# 標準入力が空なのでコマンドは実行されない
$ echo -n "" | ifne wall
-n
をつけると逆の動作になります(標準入力が空のときにコマンドが実行される)。
# 標準入力があるのでコマンドは実行されない(標準入力はそのままエコーされる)
$ echo -n hoge | ifne -n wall "empty"
hoge
# 標準入力が空なのでコマンドが実行される
$ echo -n "" | ifne -n wall "empty"
Broadcast message from ore@ore-no-server (Tue Jan 12 22:22:30 2016):
empty
man
には下記のような例が乗っています。
$ find . -name core | ifne mail -s "Core files found" root
find
コマンドの結果が空ではないときだけ mail
コマンドが実行されます。
もし ifne
を使わなかったら、find
の結果が空だと空のメールが送信されてしまいます。
isutf8
ファイルが妥当な UTF-8 のファイルかどうかを調べます。
$ echo "あいうえお" > utf8.txt
$ nkf -s utf8.txt > sjis.txt
$ isutf8 utf8.txt ; echo $?
0
$ isutf8 sjis.txt ; echo $?
sjis.txt: line 1, char 1, byte offset 6: invalid UTF-8 code
1
lckdo
複数プロセスの同時実行を防止するためにロック付きでコマンドを実行する。
flock
コマンドで同じことできるから deprecated です。
mispipe
2つのコマンドをパイプで繋げて実行し、最初のコマンドの終了コードを終了コードとして返す。
下記の例だと ls -l
の結果が sed
にパイプされて終了コードは 9 になります。
$ mispipe 'ls -l; exit 9' 'sed "s/^/ore:/"'; echo $?
例えば、次のように chronic
と組み合わせると、ログに書き込みつつコマンドが失敗したときだけメールで通知する、などとできます。
$ chronic mispipe '/path/to/command 2>&1' 'logger -s'
pee
tee
のパイプ版です。tee
は標準入力をファイルに出力するのに対して、pee
は指定されたコマンドを実行して、そのパイプに出力します。
例えば次のようにすると、echo
した内容をメールで送信しつつ wall
に流せます。
$ echo oreore | pee 'mail -s areare root' 'wall'
sponge
標準入力をすべて読み込んでから、指定されたファイルを開いて標準入力の値を書き込みます。
例えば、シェルのリダイレクトで入力ファイルと出力ファイルを同じにすると、出力が空になってしまいますが、sponge
を使えばその問題を回避できます。
# 空になってしまう
$ cat input.txt | sort > input.txt
# sponge なら大丈夫
$ cat input.txt | sort | sponge input.txt
ts
入力行にタイムスタンプを付与します。
$ ls / | ts '%Y/%m/%d %H:%M:%S:'
2016/01/12 22:51:42: bin
2016/01/12 22:51:42: boot
2016/01/12 22:51:42: dev
2016/01/12 22:51:42: etc
2016/01/12 22:51:42: home
:
日時のフォーマットには strftime
と同じものが使えます。
また、%.s
や %.S
で、秒の単位を小数にすることができます。
$ ls / | ts '%H:%M:%.S:'
23:11:17.501697: bin
23:11:17.501800: boot
23:11:17.501838: dev
23:11:17.501854: etc
23:11:17.501870: home
:
$ ls / | ts '%.s:'
1452607906.866799: bin
1452607906.866924: boot
1452607906.866981: dev
1452607906.867000: etc
1452607906.867018: home
:
-r
を指定すると、標準入力から入ってきた行のタイプスタンプと解釈できる部分を 15m5s ago
のような相対時間に置換します。
次のようになります。
$ echo "Jan 13 10:16:31: hoge" | ts -r
26m12s ago: hoge
例えば、syslog のログを相対時間にすることができます。
$ sudo tail -3 /var/log/messages | ts -r
4m14s ago ore-no-server systemd: Starting Session 182 of user root.
4m14s ago ore-no-server systemd: Removed slice user-0.slice.
4m14s ago ore-no-server systemd: Stopping user-0.slice.
Apache のアクセスログなんかも相対時間に変換できるようです。
-r
と日時フォーマットの両方を指定すると日時形式の変換ができます。例えば、syslog のログを次のように変換したりできます。
$ sudo tail -3 /var/log/messages | ts -r "[%Y/%m/%d %H:%M:%S]"
[2016/01/13 10:50:01] mzk systemd: Starting Session 183 of user root.
[2016/01/13 10:50:01] mzk systemd: Removed slice user-0.slice.
[2016/01/13 10:50:01] mzk systemd: Stopping user-0.slice.
-i
を指定すると、タイムスタンプの各出力の増分が追記されます。
$ (for x in {1..3}; do echo $x; sleep $x; done; echo done) | ts -i
00:00:00 1
00:00:01 2
00:00:02 3
00:00:03 done
%.s
や %.S
も使用可能です。
$ (for x in {1..3}; do echo $x; usleep $(($x*100000)); done; echo done) | ts -i '%.S'
00.000017 1
00.087754 2
00.201852 3
00.301900 done
vidir
ディレクトリを編集します。
例えば、次のようにファイル・ディレクトリがあるとします。
$ touch aaa bbb ccc
$ mkdir xxx yyy zzz
vidir
と実行すると、次の内容がエディタで開かれます。
1 ./aaa
2 ./bbb
3 ./ccc
4 ./xxx
5 ./yyy
6 ./zzz
次のように編集して保存します。
1 ./aaa_ore
2 ./bbb_are
3 ./ccc_sore
4 ./xxx_dore
5 ./yyy_kore
6 ./zzz_mare
ファイル・ディレクトリが次のようにリネームされます。
$ ll
total 0
-rw-r--r-- 1 ore wheel 0 Jan 12 23:17 aaa_ore
-rw-r--r-- 1 ore wheel 0 Jan 12 23:17 bbb_are
-rw-r--r-- 1 ore wheel 0 Jan 12 23:17 ccc_sore
drwxr-xr-x 2 ore wheel 6 Jan 12 23:17 xxx_dore
drwxr-xr-x 2 ore wheel 6 Jan 12 23:17 yyy_kore
drwxr-xr-x 2 ore wheel 6 Jan 12 23:17 zzz_mare
次のようにリネームするファイルやディレクトリを引数で指定したり、
$ vidir *.txt
標準入力から受け取ったりもできます。
$ find | vidir -
最後の例だとサブディレクトリの中のファイルもリネームすることができます。
vipe
シェルのパイプラインの途中でエディタを起動します。
次のようにすると command1
の結果がエディタで開かれて、エディタで編集した内容が command2
に流れます。
$ command1 | vipe | command2
zrun
引数に指定されているアーカイブを自動的に解凍してコマンドを実行します。
次のようにすると aaa.gz と bbb.gz がテンポラリに解凍されて、ファイル名が置き換えられてコマンドが実行されます。
$ zrun command aaa.gz bbb.gz
概ね次のようなことが行われていると思えばよいでしょう。
$ aaa="$(mktemp)"
$ bbb="$(mktemp)"
$ gzip -dc aaa.gz > "$aaa"
$ gzip -dc bbb.gz > "$bbb"
$ command "$aaa" "$bbb"
$ rm -f "$aaa" "$bbb"
アーカイブ形式は拡張子で判断されており、 gz bz2 Z xz lzma lzo などがサポートされているようです。