全力で怠けたい

怠けるために全力を尽くしたいブログ。

sed コマンドを使って cat, grep, tr, wc, head, tail コマンドを再発明してみた。

社内でシェルスクリプトの活用が増えるといいなー、と思って、とっかかりになるようなパズル的なものを考えてみた。 シェルスクリプトに興味をもってもらうことが目的なので、いわゆる「シェル芸」のような結構手応えがあるものではなく、一部を除いてはごく簡単なものになっていると思う。

sed コマンドのバージョン

macOSGNU sed をインストールして使っている。

$ sed --version
sed (GNU sed) 4.8

sed コマンドでいろんなコマンドを再発明する

cat

cat コマンドの再発明、そして他のコマンドの再発明には、次のファイルを入力として使う。

$ cat numbers.txt
1 2 3
4 5 6
7 8 9
10 11 12
13 14 15
16 17 18

sed コマンドは式を与えられない場合、入力をそのまま出力するので、それを利用する。

$ sed '' numbers.txt
1 2 3
4 5 6
7 8 9
10 11 12
13 14 15
16 17 18

grep

次のようにすると grep コマンドを再発明できる。

  1. -n オプションを指定し、出力を抑制する
  2. 出力したい行 (アドレス) を正規表現で指定し、マッチしたら p コマンドで出力する
$ grep 3 numbers.txt
1 2 3
13 14 15

$ sed -n /3/p numbers.txt
1 2 3
13 14 15

tr

次のようにすると tr コマンドを再発明できる。

  1. y コマンドを使って文字列内の文字を変換する
$ tr [123] [abc] < numbers.txt
a b c
4 5 6
7 8 9
a0 aa ab
ac a4 a5
a6 a7 a8

$ sed 'y/123/abc/' numbers.txt
a b c
4 5 6
7 8 9
a0 aa ab
ac a4 a5
a6 a7 a8

wc

次のようにすると wc -l コマンドを再発明できる。

  1. -n オプションを指定し、出力を抑制する
  2. 入力の最終行をマッチさせて = コマンドを実行し、マッチした行つまり最終行の行番号を出力する
$ wc -l numbers.txt
       6 numbers.txt

$ sed -n '$=' numbers.txt
6

次のようにすると head コマンドを再発明できる。

  1. 表示したい最終行 (入力全体での2行目) をマッチさせて q コマンドを実行し、そこでスクリプトを終了する
$ head -n 2 numbers.txt
1 2 3
4 5 6

$ sed '2q' numbers.txt
1 2 3
4 5 6

別解。

  1. -n オプションを指定し、出力を抑制する
  2. 表示したい最初の行から最終行までをマッチさせて、p コマンドで出力する
$ sed -n '1,2p' numbers.txt
1 2 3
4 5 6

tail

次のようにすると tail コマンドを再発明できる *1 ここでは、ファイルの末尾2行を表示させている。

  1. :a にてラベルを定義する。あとでジャンプのターゲットとして使う
  2. $q;N;3,$D;ba: この部分には以下の4つのコマンドが含まれている
    1. $q: 直前に読み込んだ行が入力の最終行である場合、それまでの行を出力して q コマンドでスクリプトを終了する。 これにより、最後の2行まで読み込んだ後に出力されるようになる。
    2. N: 次の行を読み込んでパターンスペースに追加する。 これにより、複数の行をパターンスペースに保持させ、最終的には最後の2行がパターンスペースに格納される。
    3. 3,$D: パターンスペース内の行が3行以上になった場合、最も古い行 (つまり先頭の行) を削除する。 さらに D コマンドの副作用として新しい入力行を読み込み、スクリプトが最初から実行される。 これにより、最後の2行だけがパターンスペースに保持されるようになる。
    4. ba: b コマンド (分岐コマンド) を実行して定義済みのラベル a にジャンプする。 これにより、スクリプトが最初から繰り返されるようになる。
$ tail -n 2 numbers.txt
13 14 15
16 17 18

$ sed ':a;$q;N;3,$D;ba' numbers.txt
13 14 15
16 17 18

以上。

*1:パッと思いつかなくて、コマンドのリファレンスを調べたり ChatGPT に相談しながらやってみた