Wed Dec 11 2019

おはようございます. これは Shell Script Advent Calendar 2019 の 11 日目です.

Atcoder Selection for Sed Beginners

まえおき

純粋なシェルスクリプトというのは無く(知らないけど)、 例えば $X という変数が 1 をチェックするようなコード

if [ $X -eq 1 ]; then
  hogehogehoge...
fi

にはコマンド [ が使われていたりします. 試しに手元の bash で which [ すると

$ which [
/usr/bin/[

と返ってきて、確かに外部コマンドであることが分かります. (ちなみに zsh だと built-in でした.)

このようにシェルスクリプトというのは、基本的には外のコマンドを呼び出しながらデータを処理する言語です. それでもシェルスクリプトとの親和性というのが考えられ、特に親和性が高いのが行志向で処理するようなコマンドでしょう. その一つが sed です.

よく使われる sed の例がたぶんこんなん. 良い例ではないですが.

$ cat test.txt
function fuga(x) {
    return 42
}
print fuga(1)
print fuga(2)
print [fuga("3"), fuga("4")]

$ cat test.txt | sed 's/fuga/hoge/g'
function hoge(x) {
    return 42
}
print hoge(1)
print hoge(2)
print hoge("3")
print [hoge("3"), hoge("4")]

文字列の置換です. s/.../.../g は1個目の ... を2つ目の ... に置換するという意味. 最後の g はこの置換を全て行うということで、これがない場合は各行の最初の1個にしか適用されない.

$ cat test.txt | sed 's/fuga/hoge/'
function hoge(x) {
    return 42
}
print hoge(1)
print hoge(2)
print [hoge("3"), fuga("4")]

なんで各行なのかというと, そもそも sed はコード(コマンド引数として渡された文字列)を各行に適用するものだから.

データは行ごとに分割された要素の列. sed はコードを列の各要素に適用して返す.

$ cat data
x1
x2
x3
:
xN

$ cat data | sed f
f(x1)
f(x2)
f(x3)
:
f(xN)

これが行志向と先に書いた意味です. シェルスクリプトに登場するコマンドはこの形式なことが多くて、 while read ...; do ...; done でも行ごとの処理が出来るし、 awk コマンドなんかもそうだし.

というわけで sed を使いこなせることはシェルスクリプトを使いこなすことなので、皆さん sed を勉強しましょう.

言語を学習するのに便利なサイトがあって、AtCoder っていうんですけど. これの “AtCoder Beginner Contest” の A 問題と B 問題くらいなら sed で解けます. A とか B とかは単に各コンテストの中の問題の番号ですが、若いほど簡単です. もっと正確には、各問題には点数が割り振られてるのでそれを参考にしてください.

とはいえ、 sed は基本的に文字列を処理するものであって整数や小数の演算をするものではないので、そういう問題だったらA問題であっても捨てればいいです.

本題

というわけで、sed を学ぶにあたって問題をいくつか選定してみました. 簡単な順です.

問題へのリンクと、実装すべき内容だけを簡単に紹介します. 内容自体はどれも簡単で sed でなくて普段使い慣れてる言語であればどれでも書くことはすぐに分かるようなものだと思います. (このくらいはネタバレにならないと信じてます)

最後に、私自身の解答と、簡単な解説を置いておきます. 自分で考えたい方は見ないように気をつけてください.

Atcoder Selection for Sed Beginners

ループを使うような問題もないし、ちょっと偏りがありますね。。。 b コマンドと y コマンド、あとホールドスペースを使うくらいのものを補足したい。

解答例 + 簡単な解説

s コマンドの e フラグ (s/.../.../e) が強力で、これで結局 sed の中からもシェルスクリプトが書けます. もちろんシェルスクリプトから sed を呼ぶことももちろん出来ます. 互いが互いを呼べるので表現力が全く等しい、これは同一の言語だと言っても差し支えありませんね.