-
- edハンズオン
-
- sedハンズオン
-
- awkハンズオン
sed / awkの使い方を説明するにあたって、まず ed
コマンドについて知っていただく必要があります。
ed
は著名なラインエディタで、これをベースとしてsedやex & vimなどが開発されました。
ファイルを開くと、対話型のエディタが開きます。
ed ファイル名
試しに、 ed romeo_and_juliet.md
と入力してみると、まずファイル内の文字数が表示されます。
これだけでは、何が起こっているかわからないので、ファイルの中身を探索してみましょう。
1p
と入力すると、1行目の内容が表示されます。
2p
と入力すると、2行目の内容が表示されます。
ここでの 1
, 2
はファイル内の固有の位置を示すアドレスとなっており、その後にコマンド名 p
が続いています。
p
は、指定されたアドレスの内容を出力するコマンドなので、上記のような挙動になっていました。
最初のポイント
edへの命令は、 `アドレス+コマンド` で構成されている
また、コマンドを実行すると、処理中の行が移動します。
また、アドレスはOptionalなので、コマンドのみで実行可能です。
2p
を実行した後に p
を実行すると、処理中の行が2行目に移っているので、その行が出力されることがわかります。
アドレスには複数種類があります。よく使いそうなものを挙げておきます。
1
,2
などの数値で行番号を指定します
/Romeo/
などの正規表現で行を指定します
.
=> 現在の行$
=> 最後の行-
=> 直前の行-n
=> n個前の行+
=> 直後の行+n
=> n個後の行
man ed
にわかりやすく書かれているので、困ったら参照してみてください。
/Romeo/p
と入れてみてください。
すると、次にヒットする Romeo
の行が表示されます。何度か試しに実行してみてください。(/Rom/くらいでいけます)
2つのアドレスを ,
でつなぐことで使えます。
1,3p
と入れれば 1~3行目がprintされる
1,$p
と入れれば 1行目から最終行までがprintされる
2,/Romeo/p
と入れれば、1行目から次のRomeoまでがprintされる
/ROMEO/,/JULIET/p
と入れれば、次のROMEOからその次のJULIETまでがprintされる
p
コマンドしか登場しませんでしたが、ここでいくつか追加のコマンドを紹介します。
=
=> 指定した位置のアドレスを表示する。.=
と入れると現在位置のアドレスが見られて便利。n
=> 行番号付きでprintする。めちゃめちゃ便利。h
=> 直前のエラー内容を出力する。?
が出た時に使う。H
=>h
を入力しなくても常にエラー内容を出力する- 起動時に
H
を打っておくのがおすすめ
- 起動時に
- s/re/re/ => 正規表現での置換。アドレスで指定された行内で初めてマッチした正規表現の対象を置き換える。
- s/re/re/g => 正規表現でのグローバルな置換。アドレスで指定された行内でマッチした全ての正規表現の対象を置き換える。
例) /Romeo/ を /Hoge/ に置き換えてみる
54p
と入力してみましょう。
すると、 O Romeo, Romeo! wherefore art thou Romeo?
と言う Romeo
が3回登場する行が見つかります。
ここで、 54s/Romeo/Hoge/
と入力してみましょう。
結果を p
で確認すると、 O Hoge, Romeo! wherefore art thou Romeo?
に変更されていることがわかります。
次に、グローバル置換を紹介します。
54s/Romeo/Hoge/g
を実行してみましょう。
結果を p
で確認すると、行内のRomeoが全て置き換えられ、 O Hoge, Hoge! wherefore art thou Hoge?
に変更されていることがわかります。
i
=> 挿入。アドレスで指定された行に文字列を挿入する。範囲指定可能。実行するとInsert modeに入る。a
=> 追加。次の行に文字列を追加する。実行するとInsert modeに入る。d
=> 削除。アドレスで指定された行を削除する。範囲指定可能c
=> 変更。アドレスで指定された行を置き換える。範囲指定可能。実行するとInsert modeに入る。u
=> 直前の操作を取り消す。w
=> 保存するq
=> エディタを終了する
Insert modeからは ^C で抜けることが出来ます。 Insert mode中では、複数行のテキストを入力可能で、あくまで行単位での編集となるので、行が挿入、追加、置換(変更)されることになります。
ed にはこれだけのコマンドが揃っているので、一通りのテキスト編集が実施可能です。 それでは、実際にコードを編集してみましょう。
ed ed1.go
編集した wq
で保存した結果、 go run ed1.go
が通るようになればOKです。
新規でファイルを作成したい場合は、
ed ファイル名
でファイルを作成し、 i
で内容を挿入して wq
で抜ければOKです。
edの命令は アドレス+コマンド
で実行できると言う話をしました。
実は、グローバルコマンド g/re/command-list
を使うと、ファイル全体の行に対してコマンドを実行できるという、コマンドを受け取るコマンドがあります。
これは、特にファイル全体でマッチする正規表現を置き換えたい時に便利です。
次のファイルを編集してみてください。
ed ed2.go
地獄のような内容になっているかと思います。
これを実行できるようにするには、 Brintln
を全て Println
に置き換える必要があります。
ここで、 g/Brintln/s/Brintln/Println/g
を実行すると、全ての Brintln
を Println
に置き換えることが出来ます。
省略記法として、 g/Brintln/s//Println/g
も使うことが出来ます。
あとはプログラムに問題のある箇所があるので、それを修正して go run ed2.go
が動くことを確認してみてください。
grep
コマンドの名前の由来は、edコマンドの g/re/p
らしいです。
echo 'g/Romeo/p' | ed -s romeo_and_juliet.md
してみると、なるほど感が得られると思います。
1,$
のrangeに対するエイリアスとして %
が存在します。
%p
と実行すると、ファイル全体を表示することが出来ます。
ここで、これを使って、ファイル全体の中でマッチする表現を置換するコマンドを実行しようとすると、
%s/Romeo/Juliet/g
といった形になります。
この形式のコマンドに見覚えのある方も多いのではないでしょうか? (Vimでよく使う置換コマンドです)
ついにここでsedに入ります。
説明をかなりサボってしまいますが、 sedはほぼedです
。
違いは、edが行単位での編集を志向するツールである一方、sedはファイル単位の編集を志向しているところです。
もう一つの違いは、edがアドレスを操作対象の指定に使っていた一方で、sedはアドレスを操作対象の絞り込みに使っているところです。 edでは、アドレスを指定しないと編集操作が出来ませんでした。sedでは、全行が操作対象になってしまうので、それを避けるためにアドレス指定を利用します。
実際に使ってみるのが早いのでやってみましょう。
sedの使い方は簡単です。 edの命令部を引数として与え、それを適用するファイルを指定するだけです。
sed '' sed1.go
sedは、デフォルトで操作結果の行を全てprintするようになっているので、空の命令を与えると全行がそのまま出力されます。
これを抑制したい場合は、 sed -n
を使います。
sed -n '' sed1.go
は何も表示しませんが、
sed -n 'p' sed1.go
は全行を表示します。edっぽさが顔を出しましたね。
続いて、 Brintln
の置換操作をしてみましょう。
sed 's/Brintln/Println/' sed1.go
を実行すると、行にマッチした1つ目のBrintlnが置き換えられ、
sed 's/Brintln/Println/g' sed1.go
を実行すると、行にマッチした全てのBrintlnが置き換えられることがわかります。
次に、アドレスを使ってみましょう。
sed '6,7s/Brintln/Println/g' sed1.go
を実行すると、6~7行目のBrintlnだけがPrintlnに置き換えられます。
sed '6,7d' sed1.go
を実行すると、2~4行目が削除されます。
sedへの命令は複数渡すことが出来ます。
sed '6,7s/Brintln/Println/g;8,9s/Hello/Hoge/g' sed1.go
のように ;
繋ぎで命令を書くと、67行目のBrintlnがPrintlnに、89行目のHelloがHogeに置き換えられます。
と言う形で、edのアドレス&コマンドの操作をそのままファイル単位で使えるのがsedという事が伝わったかと思います。 (実際には、使えないコマンドもかなり多いようです。sedの方がコマンドが少ない…。)
最後に、 sed1.go
を修正した結果を sed1-ans.go
に出力して go run sed1-ans.go
が実行できたら完了です。
sed '???' sed1.go > sed1-ans.go
awkも、sedと同様なファイル単位での編集を志向するツールです。 大きな違いは、awkがプログラミング言語として実装されており、より高度な操作が出来ることです。 構文的にもプログラミング言語と感じられる見た目をしています。
下記のコマンドを実行してみてください。
awk '{print}' awk1.go
awkは、明示的にprintを行うよう指定しないと、何も出力しません。
awk '' awk1.go
アドレスについては、正規表現アドレスはed / sedと共通です。
awk '/Brintln/{print}' awk1.go
awkは、
をセパレータとして、入力を分割する機能を持ちます。
awk '{ print $0 }' awk1.go
は行全体、
awk '{ print $1 }' awk1.go
awk '{ print $2 }' awk1.go
はスペース区切りです。
セパレータを変えるには、 -F
を使います。
-F,
とすると、 ,
がセパレータになります。
awk -F, '{ print $0 }' users.csv
は行全体、
awk -F, '{ print $1 }' users.csv
awk -F, '{ print $2 }' users.csv
は,区切りです。
これを使って、クエリの生成などを行ってみてもよいでしょう。
awk -F, '{ printf("UPDATE Users SET...", $0, $1...)}
例題として、IDのみを出力するawkコマンドを書いて、 user_ids.csv
に出力してみてください。