sedコマンドを全行に実行させたくない場合は内部コマンドの適用範囲を指定しよう(含む、含まない、特定の行のみ、数行毎に、など)

Bash

はじめに

シェルスクリプトでデータを絞り込む方法いろいろ(grep、sed、awk)sed コマンドを使った絞り込み方法について触れたので、 sed コマンドに関してのみもう少し深掘りしてみたいと思います。

検証環境

$ uname -moi
x86_64 MacBookPro11,4 Darwin

$ bash -version | head -n 1
GNU bash, バージョン 5.0.2(1)-release (x86_64-apple-darwin18.2.0)

$ sed --version | head -n 1
sed (GNU sed) 4.7

sedの内部コマンド

sed コマンドは標準入力あるいはファイルに対して、 内部コマンド を指定しその結果を出力する、というコマンドです。
ややこしいですが、 sed の中だけで使えるコマンド、という意味です。
内部コマンド は全行に対して、行単位に実行されます。

内部コマンドの例をいくつか上げます。

  • i : 行の前に一行追加し、引数として指定した文字列を追加
  • a : 行の後に一行追加し、引数として指定した文字列を追加
  • d : 削除する
  • p : 出力する
  • s : 正規表現で置換処理する

sedの内部コマンドは全ての行に適用される

先述したとおり、適用範囲の指定がない sed のコマンドは、全ての行に対して適用されます。

# seq で 3行出力させる
$ seq 1 3
1
2
3

# seq 実行結果を sed に適用
$ seq 1 3| sed 'a test'
1
test
2
test
3
test

# 数値をxに置換
$ seq 1 3| sed 's/[0-9]/x/'
x
x
x

# 行を削除(何も出力されない)
$ seq 1 3| sed 'd'

「すべての行を置換する」といった操作の場合には想定通りですが、「行を削除する」といった操作の場合は範囲指定ができなければ意味がありません。

sedの内部コマンドの対象行を絞り込もう

強力な sed の力をさらに引き出すには、内部コマンドを適用する「行」を絞り込む方法を知る必要があります。

sedの内部コマンド実行範囲の絞り込み方法いろいろ

ようやく本題に入ります。
内部コマンドの実行範囲を指定する方法について触れていきます。

ここでは挙動が一番わかり易い d オプションを取り上げていきます。

またいつものように、検証用のデータを作成します。

cat <<'EOF' > personal_infomation.csv
1,浜崎陽治,ハマザキヨウジ,男,1974/11/03
2,奥山隆雄,オクヤマタカオ,男,1997/08/04
3,船越瑠奈,フナコシルナ,女,1966/04/16
4,玉置忠治,タマキタダハル,男,1995/07/19
5,出口美香,デクチミカ,女,1991/10/18
6,西本良之,ニシモトヨシユキ,男,1988/03/29
7,湯川麗奈,ユカワレナ,女,1996/03/05
8,荻野莉沙,オギノリサ,女,1987/06/20
9,海野博明,ウミノヒロアキ,男,1998/05/23
10,高崎淳一,タカサキジュンイチ,男,1982/05/10
EOF

単一行にコマンドを実行(ex:2行目を削除したい)

コマンドの前に数値で行を指定します。

$ cat personal_infomation.csv | sed '2d'
1,浜崎陽治,ハマザキヨウジ,男,1974/11/03
3,船越瑠奈,フナコシルナ,女,1966/04/16
4,玉置忠治,タマキタダハル,男,1995/07/19
5,出口美香,デクチミカ,女,1991/10/18
6,西本良之,ニシモトヨシユキ,男,1988/03/29
7,湯川麗奈,ユカワレナ,女,1996/03/05
8,荻野莉沙,オギノリサ,女,1987/06/20
9,海野博明,ウミノヒロアキ,男,1998/05/23
10,高崎淳一,タカサキジュンイチ,男,1982/05/10

複数行にコマンドを実行(ex:2〜6行目を削除したい)

コマンドの前に数値で行を2つ指定します。
カンマの前がコマンドの適用開始行、後ろがコマンドの適用終了行になります。

$ cat personal_infomation.csv | sed '2,6d'
1,浜崎陽治,ハマザキヨウジ,男,1974/11/03
7,湯川麗奈,ユカワレナ,女,1996/03/05
8,荻野莉沙,オギノリサ,女,1987/06/20
9,海野博明,ウミノヒロアキ,男,1998/05/23
10,高崎淳一,タカサキジュンイチ,男,1982/05/10

特定の文字を含んだ行にコマンドを実行(ex:"男"を含む行を削除したい)

コマンドの前に対象となる行に含まれる文字を "/" で囲んで指定します。

$ cat personal_infomation.csv | sed '/男/d'
3,船越瑠奈,フナコシルナ,女,1966/04/16
5,出口美香,デクチミカ,女,1991/10/18
7,湯川麗奈,ユカワレナ,女,1996/03/05
8,荻野莉沙,オギノリサ,女,1987/06/20

正規表現も使用できます。

# 1970年代、1980年代の人は除外
$ cat personal_infomation.csv | sed '/19[78][0-9]/d'
2,奥山隆雄,オクヤマタカオ,男,1997/08/04
3,船越瑠奈,フナコシルナ,女,1966/04/16
4,玉置忠治,タマキタダハル,男,1995/07/19
5,出口美香,デクチミカ,女,1991/10/18
7,湯川麗奈,ユカワレナ,女,1996/03/05
9,海野博明,ウミノヒロアキ,男,1998/05/23

任意の2つの行の間にコマンドを実行(ex:"男"を含む行から次に"男"が見つかった行までを削除したい)

"/" を指定した絞り込み方法も、 "," の前後にそれぞれ記述し適用範囲を指定する事ができます。

# 男という文字が見つかった行から次の男という文字が見つかった行まで削除
$ cat personal_infomation.csv | sed '/男/,/男/d'
3,船越瑠奈,フナコシルナ,女,1966/04/16
7,湯川麗奈,ユカワレナ,女,1996/03/05
8,荻野莉沙,オギノリサ,女,1987/06/20

行数の指定方法と組み合わせることもできます。


# 二行目から初めて男という文字が見つかるまで削除
$ cat personal_infomation.csv | sed '2,/男/d'
1,浜崎陽治,ハマザキヨウジ,男,1974/11/03
5,出口美香,デクチミカ,女,1991/10/18
6,西本良之,ニシモトヨシユキ,男,1988/03/29
7,湯川麗奈,ユカワレナ,女,1996/03/05
8,荻野莉沙,オギノリサ,女,1987/06/20
9,海野博明,ウミノヒロアキ,男,1998/05/23
10,高崎淳一,タカサキジュンイチ,男,1982/05/10

任意の行から最後のまでコマンドを適用

$ は最後の行を表す特殊なアドレスとして指定できます。

$ cat personal_infomation.csv | sed '6,$d'
1,浜崎陽治,ハマザキヨウジ,男,1974/11/03
2,奥山隆雄,オクヤマタカオ,男,1997/08/04
3,船越瑠奈,フナコシルナ,女,1966/04/16
4,玉置忠治,タマキタダハル,男,1995/07/19
5,出口美香,デクチミカ,女,1991/10/18

n行毎にコマンドを実行

0~3 のようにチルダの前後に数値を入れてやる。
上記の例だと 「3で割ったあまりが0となる行」 を指定しているので、3,6,9行目が削除される。

$ cat personal_infomation.csv | sed '0~3d'
1,浜崎陽治,ハマザキヨウジ,男,1974/11/03
2,奥山隆雄,オクヤマタカオ,男,1997/08/04
4,玉置忠治,タマキタダハル,男,1995/07/19
5,出口美香,デクチミカ,女,1991/10/18
7,湯川麗奈,ユカワレナ,女,1996/03/05
8,荻野莉沙,オギノリサ,女,1987/06/20
10,高崎淳一,タカサキジュンイチ,男,1982/05/10

$ cat personal_infomation.csv | sed '2~3d'
1,浜崎陽治,ハマザキヨウジ,男,1974/11/03
3,船越瑠奈,フナコシルナ,女,1966/04/16
4,玉置忠治,タマキタダハル,男,1995/07/19
6,西本良之,ニシモトヨシユキ,男,1988/03/29
7,湯川麗奈,ユカワレナ,女,1996/03/05
9,海野博明,ウミノヒロアキ,男,1998/05/23
10,高崎淳一,タカサキジュンイチ,男,1982/05/10

指定した条件に 該当しない 行にコマンドを実行(【否定演算】)

! を絞り込み条件に付与する。

# 2行目【以外】を消す
$ cat personal_infomation.csv | sed '2!d'
2,奥山隆雄,オクヤマタカオ,男,1997/08/04

# 2〜6行目【以外】を消す
$ cat personal_infomation.csv | sed '2,6!d'
2,奥山隆雄,オクヤマタカオ,男,1997/08/04
3,船越瑠奈,フナコシルナ,女,1966/04/16
4,玉置忠治,タマキタダハル,男,1995/07/19
5,出口美香,デクチミカ,女,1991/10/18
6,西本良之,ニシモトヨシユキ,男,1988/03/29

# "男"という文字を含む行【以外】を消す
$ cat personal_infomation.csv | sed '/男/!d'
1,浜崎陽治,ハマザキヨウジ,男,1974/11/03
2,奥山隆雄,オクヤマタカオ,男,1997/08/04
4,玉置忠治,タマキタダハル,男,1995/07/19
6,西本良之,ニシモトヨシユキ,男,1988/03/29
9,海野博明,ウミノヒロアキ,男,1998/05/23
10,高崎淳一,タカサキジュンイチ,男,1982/05/10

ひとこと

もう少し複雑な例は次回にでも。

Bash