【Linux】sedコマンド

■ はじめに

https://dk521123.hatenablog.com/entry/2019/11/22/223043

で、sedコマンドを扱ったがかなり深いので、記事を分けた。
以下の動画で勉強するといいかも。

動画
https://dotinstall.com/lessons/basic_sed

目次

【1】sedコマンド
【2】Hello worldから概念を学ぶ
 1)Hello World
 2)解説
【3】使用上の注意
 1)実行環境による差異
 2)文字に「/」などが含まれている場合
【4】文法
 1)オプション
 2)アドレスの指定方法
【5】サンプル
 例1:dコマンド : 1行目を削除
 例2:pコマンド : 3行目だけ表示
 例3:qコマンド : 3行以降は辞める
 例4:iコマンド : 3行に「-------------」を挿入
 例5:yコマンド : 文字置換(「-」=>「?」)
 例6:sコマンド : 文字列置換
【6】実例
 例1:改行コードの変換 CRLF => LF
 例2:テンプレートファイルを書き換える
 例3:文字列の抽出
 例4:Trim処理
【7】おまけ:共通処理化

【1】sedコマンド

* sed (セド) : Stream EDitor
* 文字列の置換/削除

【2】Hello worldから概念を学ぶ

* Hello World を使って、sedの概念を学ぶ
* 以下のファイルを使う

hello.csv

id,name,remarks
001,Mike,-
002,Tom,Hello
003,Smith,World
004,Nick,!!

1)Hello World

# 3行目を消す
sed -e "3d" hello.csv

id,name,remarks
001,Mike,-
003,Smith,World
004,Nick,!!

2)解説

sed -e "3d" hello.csv

[アドレス] [コマンド]
3(行目)       d (delete:削除)

やってること

[1] ファイルから1行読み込んでパターンスペースに格納
 (パターンスペース:メモリバッファ的なもの)
[2] アドレスにマッチするか?
 => マッチした場合は、コマンドを実行
[3] パターンスペースを表示

【2】使用上の注意

1)実行環境による差異

* 実行環境(OS)によって動作や使える文法が変わる

2)文字に「/」などが含まれている場合

ダメ例:文字の置き換え

# 「/」が含まれている
RELACED_PATH="/xxx/yyy/zzz.txt"
# ${RELACED_PATH}内に「/」があるから、区切り文字と違いがわからない
sed -e "s/__PATH__/${RELACED_PATH}/" template.txt > world.txt 

対応案

区切り文字「/」を「|」にする

修正例

RELACED_PATH="/xxx/yyy/zzz.txt"
# 区切り文字を「|」にする
sed -e "s|__PATH__|${RELACED_PATH}|" template.txt > world.txt 

参考文献
https://hacknote.jp/archives/8163/

【4】文法

1)オプション

# オプション オプション-フル(予想含む) 説明
1 d delete 削除
2 p print? 表示
3 i insert 挿入
4 a append 追加
5 q quit 停止
6 y ??? 文字置換
7 s string? 文字列置換

2)アドレスの指定方法

1と3行目:「;」を使う

sed -e "1d;3d" hello.csv

001,Mike,-
003,Smith,World
004,Nick,!!

全行表示:「d」を使う

sed -e "d" hello.csv

id,name,remarks
001,Mike,-
002,Tom,Hello
003,Smith,World
004,Nick,!!

【5】サンプル

例1:dコマンド : 1行目を削除

sed -e "1d" hello.csv

001,Mike,-
002,Tom,Hello
003,Smith,World
004,Nick,!!

例2:pコマンド : 3行目だけ表示

# -n :  出力コマンド以外の出力を行わない
sed -n "3p" hello.csv

例3:qコマンド : 3行以降は辞める

sed -e "3q" hello.csv

id,name,remarks
001,Mike,-
002,Tom,Hello

例4:iコマンド : 3行に「-------------」を挿入

# "【行数】u\【挿入したい内容】"
sed -e "2i\-------------" hello.csv

id,name,remarks
-------------
001,Mike,-
002,Tom,Hello
003,Smith,World
004,Nick,!!

例5:yコマンド : 文字置換(「-」=>「?」)

sed "y/-/?/" hello.csv

id,name,remarks
001,Mike,?
002,Tom,Hello
003,Smith,World
004,Nick,!!

例6:sコマンド : 文字列置換

「00」=>消す

sed "s/00//" hello.csv

id,name,remarks
1,Mike,-
2,Tom,Hello
3,Smith,World
4,Nick,!!

正規表現による置換

# "s/【正規表現】/【置換後の文字(&:正規表現で一致した文字)】/"
sed -e "s/00[1-4]/<&>/" hello.csv

id,name,remarks
<001>,Mike,-
<002>,Tom,Hello
<003>,Smith,World
<004>,Nick,!!

1行目を消す

# + 正規表現でIDとそれ以外に分けて、それ以外だけを表示する
# (カッコは、バックスラッシュでバックスラッシュでエスケープ)
sed -e "1d" -e "s/\(00[1-4]\),\(.*\)/\2/" hello.csv
Mike,-
Tom,Hello
Smith,World
Nick,!!

【6】実例

例1:改行コードの変換 CRLF => LF

sed "s/\r//g" win-newline.txt > unix-newline.txt

https://qiita.com/dharry/items/b4eead2868dddf1499f0

例2:テンプレートファイルを書き換える

# sed "s/正規表現/置換文字列/" テンプレートファイル > 置換後のファイル
sed -e "s/__HELLO__/world/" template.txt > world.txt

template.txt

ここが、__HELLO__ に置き換わる

world.txt

ここが、world に置き換わる

例3:文字列の抽出

# echo "export A..." | sed "s/export //g"
echo "export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE;export AWS_SECRET_ACCESS_KEY=ASIAIOSFODNN7EXAMPLE;export AWS_SESSION_TOKEN=AQoDYXdzEJr...PxRfiCYEXAMPLEKEY" | sed "s/export //g"
# AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE;AWS_SECRET_ACCESS_KEY=ASIAIOSFODNN7EXAMPLE;AWS_SESSION_TOKEN=AQoDYXdzEJr...PxRfiCYEXAMPLEKEY

# 他の例については、以下の関連記事を参照のこと

シェル ~ 文字列抽出あれこれ ~
https://dk521123.hatenablog.com/entry/2021/08/03/160901

例4:Trim処理

https://genzouw.com/entry/2019/02/14/081819/850/

result="$(echo "  abc   def" | sed 's/^ *\| *$//')"
echo "###${result}###"
# <出力結果>###abc   def###

【7】おまけ:共通処理化

https://dk521123.hatenablog.com/entry/2015/03/17/233124

で行った「別シェルの関数を呼ぶ」を使う

サンプル

util.sh

#!/bin/bash

replace(){
  old=${1}
  new=${2}
  input_file=${3}
  output_file=${4}
  
  sed -e "s|${old}|${new}|" ${input_file} > ${output_file}
}

main.sh

#!/bin/bash

. ./util.sh

mkdir -p temp

echo "1"
replace '__IP_ADDR__' '127.0.0.1' 'template.tmp' 'temp/temp1.tmp'
echo "return code : ${?}"

echo "2"
replace '__USER__' 'user001' 'temp/temp1.tmp' 'temp/temp2.tmp'
echo "return code : ${?}"

echo "3"
replace '__PASSWORD__' 'passeword1' 'temp/temp2.tmp' 'output.conf'
echo "return code : ${?}"

rm -rf 'temp'

template.tmp

[localhost]
__IP_ADDR__

[all:vars]
ansible_ssh_user=__USER__
ansible_ssh_pass=__PASSWORD__

コマンド

$ ./main.sh
1
return code : 0
2
return code : 0
3
return code : 0

output.conf

[localhost]
127.0.0.1

[all:vars]
ansible_ssh_user=user001
ansible_ssh_pass=passeword1

参考文献

https://www.atmarkit.co.jp/ait/articles/1610/17/news015.html

関連記事

awkコマンド
https://dk521123.hatenablog.com/entry/2019/11/22/223043
シェル 関数
https://dk521123.hatenablog.com/entry/2015/03/17/233124
JDBCシェルスクリプトでパースする
https://dk521123.hatenablog.com/entry/2020/03/24/223323
シェル ~ 文字列抽出あれこれ ~
https://dk521123.hatenablog.com/entry/2021/08/03/160901
テキスト加工 ~ sed / awk
https://dk521123.hatenablog.com/entry/2019/11/22/223043