はじめに
ps -ef | grep
を行うと、psのヘッダが出てくれない。ヘッダを出す方法は主に2通りで、ps -efにhead -1
を付けるか、grepのかわりのコマンドを使うか。
head -1をつける
head -1をつける方法その1
ps -ef | head -n 1
と本来書きたかったps -ef | grep
を;
でつなげて書く。
$ ps -ef | head -n 1; ps -ef | grep python;
UID PID PPID C STIME TTY TIME CMD
user 2374 2365 0 00:16 ? 00:00:00 python /usr/share/ibus/ui/gtk/main.py
user 2382 2365 0 00:16 ? 00:00:00 python /usr/share/ibus-anthy/engine/main.py --ibus
user 3063 2483 0 00:34 pts/1 00:00:00 grep python
head -1をつける方法その2(グループコマンド)
方法その1だとps -ef
を二度書かなければならない。グループコマンド(group command) とよばれる{ }
を利用すれば、ps -ef
を1度だけ書けばいい。グループコマンドはリストとの間が空白またはシェルのメタ文字で分かれている必要がある。
$ ps -ef | { head -n 1; grep python; }
UID PID PPID C STIME TTY TIME CMD
user 2374 2365 0 00:16 ? 00:00:00 python /usr/share/ibus/ui/gtk/main.py
user 2382 2365 0 00:16 ? 00:00:00 python /usr/share/ibus-anthy/engine/main.py --ibus
そして、方法その2にはもう一つメリットがある。通常ps -ef | grep XX
を行うと、標準出力に自分自身(つまりgrep XX
)が表示されてしまう。何度か実行したが、グループコマンドを利用すると、ps -ef | grep
の結果に自分自身は表示されなかった。
2018/11/02追記
Why does a pipe to a command group only work for some commands?でこの方法をとることが非推奨であることが分かった。
Apparently ps -e is sending the header line first, then not sending anything, then buffering the rest of the output. head must think the stream is closed after the first line, so it exits, leaving grep to see the rest.
本来であればpsの出力は一番目のhead
で処理されてgrep
には渡ってこないはず。標準出力を複数のプロセスにパイプで渡したければ、moreutilsのpee
コマンドを使用しなければいけない。
引用したように、たまたま-e
オプションの挙動のおかげでうまく動いていただけである。
2021/04/27追記(readコマンドとの組み合わせ)
awkで列番号ではなく列名でカラムを抜き出すを考えているときに思いついたが、read
コマンドとグループコマンドをあわせて使えばヘッダを表示することができる。
$ ps -ef | { read; echo "$REPLY"; grep python; }
UID PID PPID C STIME TTY TIME CMD
user 2374 2365 0 00:16 ? 00:00:00 python /usr/share/ibus/ui/gtk/main.py
user 2382 2365 0 00:16 ? 00:00:00 python /usr/share/ibus-anthy/engine/main.py --ibus
read 変数
あるいはread
を実行すると、1行だけ読み取って変数に格納してくれる(変数を指定しない場合は$REPLY
に格納される)。
そのあとにgrep
を実行すると、read
で読み込まなかった残りをgrep
が読み込んで処理する。
grepを使用しない
grepのかわりにps -f -C cmdlistを使う方法
実はps -ef | grep
と同等の機能が、grepを使わずにpsのオプションだけで実現できる。
そもそもps -ef | grep
を行うとヘッダが表示されない原因はヘッダの文字列がgrepの正規表現に合致しないためなので、grepを使わずにpsだけで実現できれば、ヘッダも表示されたままである。ただしgrepはコマンドの引数もマッチの対象にできるため、grepの方が柔軟ではある。
$ ps -f -C cmdlist
※全て表示を示す-e
はつけない。
例:
$ ps -f -C python
UID PID PPID C STIME TTY TIME CMD
user 2374 2365 0 00:16 ? 00:00:00 python /usr/share/ibus/ui/gtk/main.py
user 2382 2365 0 00:16 ? 00:00:00 python /usr/share/ibus-anthy/engine/main.py --ibus
またps -f -U userlist
等も同様の使い方ができるが、psのオプションは条件がorでつながっていくので、-C
と-U
を同時に指定はできない。柔軟さを求めるならgrepで正規表現等を使うのが一番いい。
grepのかわりにsedかawkを使う
ps -ef | sed -n '1p;/python/p'
あるいはps -ef | awk 'NR==1 || /python/'
とすると、以下のようにgrepを使用したときと同じ出力が得られる。
UID PID PPID C STIME TTY TIME CMD
user 2374 2365 0 00:16 ? 00:00:00 python /usr/share/ibus/ui/gtk/main.py
user 2382 2365 0 00:16 ? 00:00:00 python /usr/share/ibus-anthy/engine/main.py --ibus
user 3063 2483 0 00:34 pts/1 00:00:00 sed -n 1p;/zabbix/p
UID PID PPID C STIME TTY TIME CMD
user 2374 2365 0 00:16 ? 00:00:00 python /usr/share/ibus/ui/gtk/main.py
user 2382 2365 0 00:16 ? 00:00:00 python /usr/share/ibus-anthy/engine/main.py --ibus
user 3063 2483 0 00:34 pts/1 00:00:00 awk NR==1 || /zabbix/
sedでは1行目を表示し、次は正規表現にマッチするものを表示するというロジック。awkでは1行目かあるいは正規表現にマッチするものを表示するというロジック。微妙に細部は異なるものの、どちらもgrepの代用ができる。