1
2

grep・sort時にヘッダ行(一行目)を残すhdrコマンドを作ったよ

Last updated at Posted at 2023-11-09

はじめに

いきなりですが問題です。「👇ここ」は一体何の値でしょう?

$ ps -u | grep bash          👇 ここ
koichi   2925430  0.0  0.0  10804  6040 pts/7    Ss   20:31   0:02 -bash
koichi   2970810  0.0  0.0   8824  5568 pts/23   Ss   20:35   0:00 -bash
koichi   3061064  0.0  0.0   8688  4204 pts/22   Ss   21:11   0:00 -bash
koichi   3309961  0.0  0.0   8820  5832 pts/0    Ss   22:03   0:00 -bash

というような問題を解くのが嫌になったので、hdr コマンドを作りました。コマンドの前に hdr と書くだけでヘッダ行を残すことができます。

$ ps -u | hdr grep bash       👇 ここ
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
koichi   2925430  0.0  0.0  10804  6040 pts/7    Ss   20:31   0:02 -bash
koichi   2970810  0.0  0.0   8824  5568 pts/23   Ss   20:35   0:00 -bash
koichi   3061064  0.0  0.0   8688  4204 pts/22   Ss   21:11   0:00 -bash
koichi   3309961  0.0  0.0   8820  5832 pts/0    Ss   22:03   0:00 -bash

それ awk でできるよ?

面倒なので書きたくありません。hdr コマンドは対話シェル用です。シェルスクリプトの中に書く場合は以下の方法を使いましょう。

$ ps -u | awk 'NR==1 || /bash/'

上記のコードは awk を使う前提となってしまいますが、hdr コマンドは任意のコマンドを呼び出せるので rgag などと組み合わせて使うことも出来ます。また sort コマンドと組み合わせることもできます。

$ ps -u | hdr grep bash | hdr sort -k 5,5n

hdr コマンドは組み合わせて使えるコマンドを作るという Unix 哲学的な考え方のコマンドです。

hdr コマンドの使い方

使用するときは以下のコードを .bashrc なり .zshrc なりにコピペするか、. (source) コマンドで読み込んでください。

hdr() {
  ( IFS= read -r REPLY && printf '%s\n' "$REPLY" )
  "$@"
}
alias hdr="hdr "

( ... ) を使っているのは REPLY 変数をローカル化するためです。local コマンドでローカル変数にしていないのは、local 非対応の POSIX シェルにも対応するためです。REPLY 変数は bash などでは省略可能ですが、省略できない POSIX シェルもあります。head -n 1 は1行ではなくある程度のサイズを読み込むので動作しません。alias については後述します。

ヘッダが二行以上の場合にも対応したいとかヘルプを表示したいとか高機能なものが欲しい人はこちらをどうぞ

hdr() {
  case ${1:-} in
    -h | --help) set -- ;;
    --) shift && set -- 1 "$@" ;;
    *[!0-9]*) set -- 1 "$@" ;;
  esac

  if [ $# -eq 0 ]; then
    echo "Usage: hdr [num | --] [command [args...]]"
  else
    (
      while [ "$1" -gt 0 ] && IFS= read -r REPLY; do
        set -- $(($1 - 1))
        printf '%s\n' "$REPLY"
      done
    )
    shift
    "$@"
  fi
}
alias hdr="hdr "

なんでシェル関数にしたの?

普段私は特別な理由がない限り、ツールは単独のシェルスクリプトにするのですが、今回はそれでは不便だったので rc ファイルに定義するシェル関数にしました。理由はエイリアスです。

grep コマンドって普通色が付きますよね? 人によっては付かないかもしれませんが。色がつく理由は次のようなエイリアスが設定されているからです。

alias grep='grep --color=auto'

しかし、ここで hdr を単独のシェルスクリプトにしてしまうと、このエイリアスは展開されません。

    👇ここは hdr コマンドの引数なのでエイリアスが展開されない
hdr grep bash 

仕方ないので hdr をエイリアスにする必要があります。なぜ hdr をエイリアスにするという話につながるんだ?と思うかもしれませんが、エイリアスのちょっとマイナーな機能を利用しています。

alias hdr="hdr "
              👆 末尾のスペースが重要

エイリアスの設定で末尾にスペースがあると

👇 hdr のエイリアスが展開されたあとに・・・
hdr grep bash 
    👆 hdr の次の単語もエイリアス展開の対象になる

という動作をします。これにより hdr コマンドを使っても grep コマンドは以前と同様に動作を行ってくれます。この機能は前から知ってはいたのですが、なにげに初めてまともな使い方をした気がします。

実際には alias 部分だけを rc ファイルに入れれば hdr コマンドは単独のシェルスクリプトにできるのですが、この程度なら分けるよりも一つのまとめたほうが良いかなと思いました。

さいごに

前々から不便に感じてイライラしていたのですが、さっさと作っておくべきでしたね。記事を書く時間のほうが長かったです。自動化するためにシェルスクリプトがあるというのに、作る手間を惜しんで毎回手作業を行うのはシェルの正しい使い方ではありません。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2