おはようございます. これは Shell Script Advent Calendar 2019 の 11 日目です.
純粋なシェルスクリプトというのは無く(知らないけど)、 例えば $X
という変数が 1
をチェックするようなコード
にはコマンド [
が使われていたりします. 試しに手元の bash で which [
すると
と返ってきて、確かに外部コマンドであることが分かります. (ちなみに 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 はコードを列の各要素に適用して返す.
これが行志向と先に書いた意味です. シェルスクリプトに登場するコマンドはこの形式なことが多くて、 while read ...; do ...; done
でも行ごとの処理が出来るし、 awk
コマンドなんかもそうだし.
というわけで sed を使いこなせることはシェルスクリプトを使いこなすことなので、皆さん sed を勉強しましょう.
言語を学習するのに便利なサイトがあって、AtCoder っていうんですけど. これの “AtCoder Beginner Contest” の A 問題と B 問題くらいなら sed で解けます. A とか B とかは単に各コンテストの中の問題の番号ですが、若いほど簡単です. もっと正確には、各問題には点数が割り振られてるのでそれを参考にしてください.
とはいえ、 sed は基本的に文字列を処理するものであって整数や小数の演算をするものではないので、そういう問題だったらA問題であっても捨てればいいです.
というわけで、sed を学ぶにあたって問題をいくつか選定してみました. 簡単な順です.
問題へのリンクと、実装すべき内容だけを簡単に紹介します. 内容自体はどれも簡単で sed でなくて普段使い慣れてる言語であればどれでも書くことはすぐに分かるようなものだと思います. (このくらいはネタバレにならないと信じてます)
最後に、私自身の解答と、簡単な解説を置いておきます. 自分で考えたい方は見ないように気をつけてください.
ループを使うような問題もないし、ちょっと偏りがありますね。。。 b
コマンドと y
コマンド、あとホールドスペースを使うくらいのものを補足したい。
dreameraser
という文字列を dreamer + aser
としてしまって、まず dreamer
を消すと aser
だけが残って空にできなくなってしまうのが罠なんだけど、sed は最長マッチをしてくれるから(?)この罠に引っかからないc
コマンドは後ろに続く文字列に置き換えて出力して以降の処理をスキップする
cYES
を実行すると YES
を出力して即座にプログラムを終了するT + T
の形をしてるかチェックする\(.*\)\1
がそれになる1d
で実現できる
1
は一行目に次のコマンドを実行するという意味d
は与えられた行を削除 (delete) するという意味/S/
は入力に S
が含まれてれば発動するという意味
Sunny
のみcCloudy
が実行される
Cloudy
が出力されて即座に終了するeval
すればいいecho $(( 1 + 3 ))
みたいに計算できるs/.../.../e
は置換した後に出来た文字列をシェルスクリプトとしてシェルに投げて出力に変換する
echo 1 + 2 | sed 's/.*/echo $((&))/'
echo $((1 + 2))
echo 1 + 2 | sed 's/.*/echo $((&))/e'
3
echo $(( & < 3200 ))
N
コマンドは次の行を読んで今読んでる行の後ろに付け足します(改行文字を間に挟む)1
なら問答無用で cred
0
なら、今 sed が読んでるテキストは 0\ns
という形になってて s
を出せばいいので頭の 0\n
だけ消せば ok+
にして、また echo $(( & ))
で計算$(( & >= 22 ))
で大小比較s
コマンドの e
フラグ (s/.../.../e
) が強力で、これで結局 sed の中からもシェルスクリプトが書けます. もちろんシェルスクリプトから sed を呼ぶことももちろん出来ます. 互いが互いを呼べるので表現力が全く等しい、これは同一の言語だと言っても差し支えありませんね.