Skip to content

Latest commit

 

History

History
282 lines (169 loc) · 11.4 KB

README.md

File metadata and controls

282 lines (169 loc) · 11.4 KB

sed & awk ハンズオン

全体の流れ

    1. edハンズオン
    1. sedハンズオン
    1. awkハンズオン

edハンズオン

sed / awkの使い方を説明するにあたって、まず ed コマンドについて知っていただく必要があります。

ed は著名なラインエディタで、これをベースとしてsedやex & vimなどが開発されました。

edの基本的な使い方

ファイルを開くと、対話型のエディタが開きます。

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? に変更されていることがわかります。

Viを彷彿とさせるコマンド

  • 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 を実行すると、全ての BrintlnPrintln に置き換えることが出来ます。 省略記法として、 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に入ります。

説明をかなりサボってしまいますが、 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ハンズオン

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 に出力してみてください。