# GNU sed
###### tags: `bash`,`sed`,`regexp`
> ref: [GNU sed manual](https://www.gnu.org/software/sed/manual/sed.html#Numeric-Addresses)
[Toc]
# overview
1. [How sed work](https://www.gnu.org/software/sed/manual/sed.html#Execution-Cycle)
- sed有兩個buffer place: `pattern space`與`auxiliary hold space`, 初始時都是空的
- sed對目標文本的第一行執行第一個`cycle`
- sed由讀取目標文本第一行開始,暫時刪除所有trailing newline,放進pattern space
- `cycle`代表一系列對文本的`command`,sed透過`address`來判定要不要對當前的pattern space內文執行`command`,有符合`address`才執行`command`
- `address`可以是regular expression(`/.../`),一個用數字代表行數的range(第4到第10行`4,10`)
- 直到`cycle`結束後,除非有`-n`選項抑制(或者其他如`D`,`d` command), pattern space將被auto-printing至standard output
1. 預設auto-printing在cycle結束之後才發生且auto-printing前會先換行
- 除非有`d`或`D`等commands,否則某行的`pattern space`不會被刪除;而`hold space`無論如何都在.
- 但當cycle結束後,新的cycle開始,pattern space會被刷新
- 如果想保留該次cycle pattern space的內容,可以試試`H` command,將欲保留的pattern space內文放進hold space
- 注意! sed再處理第幾行與這是第幾個cycle,兩個數字不一定相同
- 可以透過`n`與`N`command在一次cycle中處裡兩行
- 可以透過`b`控制sed flow,使之在同個cycle內不停讀取下一行處理
- (see commands `h` `H` `x` `g` `G` to move data between both buffers)
- sed對下一行的`cycle`開始...
- ...
3. 一個`cycle`由使用者決定要在這個`cycle`做什麼動作,sed在每個`cycle`都有個`pattern space`預設情況下都會輸出這個`pattern space`的內容
# Commandline option
1. `-i[SUFFIX]` `--in-place=[SUFFIX]` 原本所有sed的output都是輸出到stdout,使用這個選項會將output全部輸出到以目標檔案為名的檔案,**原先內容被truncate**,但會創建一個裡面存著原始內容的back-up file
- 請愛用`--in-place==`, `-i`黏在一起很難看
- Back-file的命名:
- 可以使用`--in-place=.backup` 創建一個完整檔案名稱(包含附檔名)再加上`.backup`的back-up file.例如:
``` bash
sed -E --in-place=.backup -e 'l;=;/\[security\]/a\' -e 'suprise' /tmp/config.conf'
```
- 會幫你保留原檔,檔案路徑與檔名為 `/tmp/config.conf.backup`
2. `-r`, `-E`, `--regexp-extended`
- 使用 [Extended regular expression](https://www.gnu.org/software/sed/manual/sed.html#ERE-syntax), 就如同 egrep 用的
# Address: 決定sed的一系列動作在那(些)行上執行
## 用regexp:
- `/regexp/`
- `\%regexp%`
## 用range
- `4,6`
- `50`
- `3.$`
## 用steps
- `0~2`
- `1~2`
- `10~5`
- `50~0`
## 用range與regexp:
1. [ `/regexp/,/regexp/{commands}` ](https://hackmd.io/@3oatvhDfTSqijOwo0tingw/S1zj6NoZd#%E5%90%8C%E4%B8%8A%E4%BE%8B%E5%9C%A8shell-script%E4%B8%AD%E7%94%A8here-document%E8%A7%A3%E9%87%8B%E8%87%AA%E5%B7%B1)
- 表示從找到第一個regexp的那行,到找到第二個regexp的那行,整塊文字做什麼事
# regexp
## [BRE與ERE在sed的差別](https://www.gnu.org/software/sed/manual/html_node/BRE-vs-ERE.html)
> ref:
> [regular expression info: BRE Vs ERE in POSIX](https://www.regular-expressions.info/posix.html)
`+` `?` `(` `)` `{` `}` `|` 在regexp中有特殊意義,如果想要表達他的特殊含義,BRE情況下前面要用 `\`, ERE 則不用;如果只是想要 literal 含意, BRE 不用 `\` ,ERE 前面要加 `\`. 還有 BRE 裡沒有 `|` 的用法,所以想使用 alternative 功能,一定要用 ERE
- 如果想表達 Literal 的 `+`
- BRE內不用`\`跳脫
- ERE內需要用`\`跳脫
``` bash
echo 'a+b=c' > foo
sed -E -n '/a\+b/p' foo
sed -n '/a+b/p' foo
# a+b=c
```
- 如果想表達 `+` 特殊意義, 此例中是多個 `a`
- BRE內要用`\`
- ERE內不用`\`
``` bash
echo aab > foo
sed -n '/a\+b/p' foo
sed -E -n '/a+b/p' foo
# aab
```
## [regexp extension](https://www.gnu.org/software/sed/manual/sed.html#regexp-extensions)
BRE(basic regular expresion)與ERE(extended regular expression)皆適用.
1. `\b`,`\B` word boundary
- `\b` 會match一邊是word character`[a-zA-z_0-9]`,一邊不是word character的情形,且包含整個string的字首與字尾
``` bash
echo "abc %-= def._ ga _ fd8989" | sed 's/\b/X/g'
```
``` bash
XabcX %-= XdefX.X_X XgaX X_X Xfd8989X
```
- `\B` 會match兩邊都是word character,兩邊都不是word character的情形
``` bash
echo "abc %-= def._ ga _ fd8989" | sed 's/\B/X/g'
```
```
aXbXc X%X-X=X dXeXf._ gXa _ fXdX8X9X8X9
```
- `\b`與[awk的\b \y \B行為略有不同](https://hackmd.io/@3oatvhDfTSqijOwo0tingw/B1LP5pcSP#y-B-word-boundary)
2. `\w`, `\W` word character
- `\w` 匹配任何word character `[a-zA-z0-9_]`
- `\W` 匹配非word character
3. `\<`, `\>` start or end of word character(s)
- `\<` 匹配一個或多個word character的開頭,包含一個string的開頭
``` bash
echo "abc %)(_) oll t 4 00" | sed 's/\</X/g'
```
```
Xabc %)(X_) Xoll Xt X4 X00
```
- `\>` 匹配一個或多個word character的尾端
``` bash
echo "abc %)(_) oll t 4 00" | sed 's/\>/X/g'
```
```
abcX %)(_X) ollX tX 4X 00X
```
4. `\s`, `\S`
- `\s` Matches whitespace characters (spaces and tabs). Newlines embedded in the pattern/hold spaces will also match:
``` bash
echo "abc %-= def." | sed 's/\s/X/g'
#abcX%-=Xdef.
```
- `\S` Matches non-whitespace characters.
# 範例
``` bash
# 範例文本
echo $'here we go again\nhere is all charater,randomly separated\n`~!@#$\n%^&*\n()_-+=,.\n/?<>{}[]():;\n\'"|\\'
```
```
here we go again
here is all charater,randomly separated
`~!@#$
%^&*
()_-+=,.
/?<>{}[]():;
'"|\
```
## `q` 在第幾行後sed直接quit離開
1. `q` 在第幾行後sed直接quit離開,但是pattern space2的auto-printing沒關的話,依然會有pattern space被印出來. 如果連上述的行為都不要的話,但是又不想用`-n`選項,可以考慮用`d`
``` conf
[agent]
logdir=/var/log/ambari-agent
system_resource_overrides=/etc/resource_overrides
[security]
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
```
``` bash
sed '5q' /etc/ambari-agent/conf/ambari-agent.ini
```
- output:
```
[agent]
logdir=/var/log/ambari-agent
system_resource_overrides=/etc/resource_overrides
[security]
```
- 如果加上`-n`抑制輸出pattern space,則會什麼都沒有輸出
## `d` 刪掉某行的pattern space
1. `d` 淨空當前cycle的pattern space,取消anto-printing(儘管auto-printing有開),在當前cycle中剩下的動作也一併取消,之後進下一行開始一個新的cycle
``` conf=
[agent]
logdir=/var/log/ambari-agent
system_resource_overrides=/etc/resource_overrides
[security]
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
```
``` bash
sed '5q;2d;l' /etc/ambari-agent/conf/ambari-agent.ini
```
- output:
```
[agent]
system_resource_overrides=/etc/resource_overrides
[security]
```
2. 刪掉空行:
``` bash
sed -E '/^\s*$/d'
```
## `=` debug: 印出目前sed停在哪一行
1. `=` 印出sed的current input line在文本的第幾行,印出行數後再印個newline character(`\n`)換行
## `ln` unambiguous printing
1. `l` 以unambiguous的方式打印當前pattern space內容:
- 印不出來的字元用C-style與backslash`\`跳脫來表示
- 在該pattern space打印完成後,會以`$`表示結束
- 在打印時如果字元太長,不易閱讀,可給予`n`,表示用backslash`\`來摺疊一個很長的行,`n`是附加的,用來表示在打印時每幾個字元要切成一行,且數量**包含**用來摺疊的反斜線`\`
- n=4,要印`abcd`,第一行會是`abc\`,第二行會是`d$`
- n如果是0表示讓長長的一行直接印出來,不用切
- n沒給則以`sed` commandline option `-l N`或說`--line-lebgth=N` 為預設,而此command line的預設值是70
``` conf=
[agent]
logdir=/var/log/ambari-agent
system_resource_overrides=/etc/resource_overrides
[security]
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
```
``` bash
sed '5q;2d;l9' /etc/ambari-agent/conf/ambari-agent.ini
```
- output
```=
[agent]$
[agent]
system_r\
esource_\
override\
s=/etc/r\
esource_\
override\
s$
system_resource_overrides=/etc/resource_overrides
$
[security]
```
- 第1行output來自第1個cycle的`l9`,unambiguous printing
- 第2行output來自第1個cycle結束後的auto-printing, 接著文本中的第2行直接被第2個cycle的`2d`刪掉
- 第3~9行output來自第3個cycle的`l9`,印出時每行9個字元(包含摺疊用的`\`),該cycle的pattern space印完後已`$`結尾
- 第10行output來自第3個cycle結束後的auto-printing
- 第11行output來自第4個cycle的`l9`,pattern space是空的(因為是空行)依然以`$`作結
- 第12行output來自第4個cycle結束後的auto-printing,接著sed因為第5個cycle的`5q`而exit(剩下的command不執行)
- 第13行output來自第5個cycle結束後的auto-printing
## `p` 再次輸出該cycle的pattern space至standard output
``` conf=
[agent]
logdir=/var/log/ambari-agent
system_resource_overrides=/etc/resource_overrides
[security]
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
```
1. (通常與`-n`連用)我只想看第3行到最後
``` bash
sed -n -e '3,$p' /etc/ambari-agent/conf/ambari-agent.ini
```
3. 第5行後sed離開,強制1~6行pattern space印出,刪掉1~2行pattern space,強制1~6行pattern space印出,匹配某行後加上text,抑制輸出預設的pattern space
``` bash
sed -n \
-e '/^system_resource_overrides.*$/a\' -e 'surpriseMotherfucker' \
-e '1,6p;1,2d;1,6p;5q' \
/etc/ambari-agent/conf/ambari-agent.ini
```
- output:
```=
[agent]
logdir=/var/log/ambari-agent
system_resource_overrides=/etc/resource_overrides
system_resource_overrides=/etc/resource_overrides
surpriseMotherfucker
[security]
[security]
```
- `5q`最優先決定sed要執行到哪裡,所以在第5行之後的動作都將被忽略
- 第一個`1,6p`將1~6行的pattern space再次印一次
- `d`與`p`動作同等,先寫後寫順序有差
- `1,2d` 1~2行的pattern space被拿掉
- 第二個`1,6p`將1~6行的pattern space再次印一次,但1~2行的已經pattern space已經被拿掉了,所以1~2行沒有再印一次
- 最後,只對文本進行匹配,成功後在其後插入text
- `-n`抑制輸出預設的pattern space,所以確定所有stdout都是`p`與`a`的傑作
## `n` 用來跳過行數
- 將當前 pattern space 內的文字換成下一行文字, 且 sed 所在的行數也會變成下一行
- 如果沒有下一行了, `n` 會使 sed 直接離開
- 範例:
:::spoiler 基本
``` bash
echo $'test\ntest2\ntest3\ntest4\ntest5' | sed -n '{=;l;n;=}'
```
``` bash=
1
test$
2
3
test3$
4
5
test5$
```
- 第1行 output 由第一個 cycle 的第一個 `=` 形成, 顯示 sed 在第一行.
- 第2行 output 由第一個 cycle 的 `l` 形成, 顯示目前 pattern space 裡的文字為何
- 接著第一個 cycle 的 `n` 將 sed "處理第幾行"的行號加 1, 變成 2
- 第3行 output 由第一個 cycle 的第二個 `=` 形成, 顯示目前 sed 正處理第 2 行
- 接著第一個 cycle 結束, 由於沒有 auto-printing, 所以不會自動輸出 pattern space 裡的文字
- 接著第二個 cycle 開始, pattern space 內換上第三行的文字
- 第4行 output 由第二個 cycle 的第一個 `=` 形成, 顯示目前 sed 正處理第 3 行
- 第5行 output 由第二個 cycle 的 `l` 形成, 顯示目前 pattern space 內的文字
- 接著第二個 cycle 的 `n` 將 sed "處理第幾行"的行號加 1, 變成 4
- 第6行 output 由第二個 cycle 的第二個 `=` 形成, 顯示目前 sed 正處理第 4 行
- 接著第二個 cycle 結束, 由於沒有 auto-printing, 所以不會自動輸出 pattern space 裡的文字
- 接著第三個 cycle 開始, pattern space 內換上第五行的文字
- 第7行 output 由第三個 cycle 的第一個 `=` 形成, 顯示目前 sed 正處理第 5 行
- 第8行 output 由第三個 cycle 的 `l` 形成, 顯示目前 pattern space 內的文字
- 接著第三個 cycle 的 `n` 使 sed 直接離開(不做剩下的動作), 因為已經沒有下一行了, 且由於沒有 auto-printing, 所以不會再次輸出 pattern space 內的文字
:::
::: spoiler 文件中每隔三行插入文字
``` conf=
[agent]
logdir=/var/log/ambari-agent
system_resource_overrides=/etc/resource_overrides
[security]
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
```
``` bash
sed -e 'n;n' -e 'a\' -e 'surpriseMotherFucker' /tmp/conf
```
- output 與分析
:::spoiler
```=
[agent]
logdir=/var/log/ambari-agent
system_resource_overrides=/etc/resource_overrides
surpriseMotherFucker
[security]
keysdir=/var/lib/ambari-agent/keys
surpriseMotherFucker
server_crt=ca.crt
```
- 第1行output來自: 第1個cycle, pattern space裝第一行文字,第1個`n`,接著把pattern space內文換成第二行文字
- 第2行output來自: 第1個cycle, pattern space裝第二行文字,第2個`n`,接著把pattern space內文換成第三行文字
- 第3行output來自: 第1個cycle的auto-printing,而pattern space裡面裝著第3行文字
- 第4行output來自: 第1個cycle的`a\`,在cycle的auto-printing結束之後,output`a\`後面指定的文字到stdout
:::
:::
## `a\` 用regexp匹配某行後,在行後插入text
1. `a\` 當cycle運行到`a\`時,會先跳過. 在該cycle結束之後,接著沒有`-n`抑制,並且auto-print pattern space,之後,輸出 `a\`指定的`text`到standard output
- `a`代表append, append text的意思
- 注意與auto-printing無關
- `a\` 後的`text`可以使用Bash的`$`變數代換,argument需要轉用雙引號如:
``` bash
sed -e '/^bc*$/a\' -e "$THE_BASH_BAR" /path/to/file
```
- 如果 sed 同時啟用了`-i`選項(意指直接對檔案動作),則會在符合`a\`的address(例如某regexp)該行之後,插入所指定的`text`
- 有關`-i` `--in-place`的說明請詳[sed GNU commmandline option](https://www.gnu.org/software/sed/manual/sed.html#Command_002dLine-Options)
2. 可以嘗試用`s///`來模仿`a\`的功能:
1. 在任何非空行者的下一行加入字串:
``` bash
echo $'a\nb\nc\n' | sed '/./s/$/\nsurpriseMotherFucker/'
```
3. 範例文件: `/etc/ambari-agent/conf/ambari-agent.ini`
``` conf=
[agent]
logdir=/var/log/ambari-agent
system_resource_overrides=/etc/resource_overrides
[security]
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
```
1. 匹配`[security]`之後加上新的配置`force_https_protocol=PROTOCOL_TLSv1_2`
``` bash
THE_CONF=`echo 'force_https_protocol=PROTOCOL_TLSv1_2'`
# cat /etc/ambari-agent/conf/ambari-agent.ini | sed -e '/\[security\]/a\' -e 'force_https_protocol=PROTOCOL_TLSv1_2'
cat /etc/ambari-agent/conf/ambari-agent.ini | sed -e '/\[security\]/a\' -e "$THE_CONF"
# sed -e '/\[security\]/a\' -e 'force_https_protocol=PROTOCOL_TLSv1_2' /etc/ambari-agent/conf/ambari-agent.ini
sed -e '/\[security\]/a\' -e "$THE_CONF" /etc/ambari-agent/conf/ambari-agent.ini
```
- 注意兩個連續的`-e`,這是GNU extention 是為了書寫清楚方便,不然不用`-e`就會變成如:(降低了可讀性)
``` bash
cat /etc/ambari-agent/conf/ambari-agent.ini | sed '/\[security\]/a\
force_https_protocol=PROTOCOL_TLSv1_2'
```
- `a\` 後面一個keyboard input enter再接要插入的字串
- 加上 `-i` 將直接修改目標文檔內容
``` bash
sed -i -e '/\[security\]/a\' -e 'force_https_protocol=PROTOCOL_TLSv1_2' /etc/ambari-agent/conf/ambari-agent.ini
```
## `r` 用regexp匹配某行後,後面插入某文件所有內容
1. `r`當cycle運行到`r`command時,會將`r`指定的文件的內容先保留起來,當該cycle運行結束,沒有`-n`所以auto-printing pattern space之後,再output文件內容至stdout
- 我覺得適合的使用時機是使用`-i`不使用`-n`,直接對目標文件進行修改,效果是在匹配目標行之後加入指定文件之內容
2. 範例文檔: `/etc/ambari-agent/conf/ambari-agent.ini`
``` conf=
[agent]
logdir=/var/log/ambari-agent
system_resource_overrides=/etc/resource_overrides
[security]
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
```
1. 匹配`[security]`之後加上`/tmp/a.txt`文件裡的內容
``` bash
# /tmp/a.txt
this is a test
this is also a test
```
``` bash
sed -i -e '/\[security\]/r /tmp/g' /etc/ambari-agent/conf/ambari-agent.ini
```
- `r` 代表read, read filename的意思
## `w` 將pattern space的內容寫進某檔案
1. `w` 當cycle運行到`w`時,會將當下的pattern space輸出至指定的檔案路徑
- GNU sed的extension另外提供兩個檔案`/dev/stderr`與`/dev/stdout`
- 如果指定的檔案不存在,將被創造
- 如果指定的檔案有內文,內文將先被truncate
## `i\` 用regexp匹配某行後,在行前插入text
1. `i\` 當cycle運行到`i\`command時,單純output`i\`後面指定的text到stdout,接著繼續cycle,最後依然有auto-printing pattern space
- 我覺得適當的使用時機是使用`-i`,但不使用`-n`直接對一個檔案做修改(例如新增配置)
2. 範例文檔: `/etc/ambari-agent/conf/ambari-agent.ini`
``` conf=
[agent]
logdir=/var/log/ambari-agent
system_resource_overrides=/etc/resource_overrides
[security]
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
```
1. 匹配`system_resource_overrides`之後,前面加入新配置
``` bash
sed -i -e '/^system_resource_overrides=.*$/i\' -e 'foo=bar' /etc/ambari-agent/conf/ambari-agent.ini
```
- `i` 代表insert, insert before的意思
## `c\` 用regexp匹配某行後,整行替換
1. `c\` 當cycle運行到`c\`command時,會把pattern space淨空,所以沒有auto-printing,接著output `c\` 後面指定的text到stdout. 之後重啟cycle,讀進下一行文字
- 注意!!! `c\` 會淨空pattern space,我覺得適合使用的時機是配合`-i`,不用`-n`,直接對一個檔案修改內容
2. 範例文檔: `/etc/ambari-agent/conf/ambari-agent.ini`
``` conf=
[agent]
logdir=/var/log/ambari-agent
system_resource_overrides=/etc/resource_overrides
[security]
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
```
1. 匹配`[security]`,整行換成`[secure]`
``` bash
sed -i -e '/\[security\]/c\' -e '[secure]' /etc/ambari-agent/conf/ambari-agent.ini
```
- `c` 表示 change(replace)的意思
## `N` 一次在pattern space內處理兩行
1. `N` 如果有下一行可以input,則附加newline character(`\n`)進pattern space後,再把下一行input line也附加進pattern space,動作結束. 如果這時候有`=`動作,位置會是在下一行的行數; 反之,
1. 如果沒有下一行,`N`會讓sed直接exit,忽略後面的動作,沒有下個cycle,最後預設auto-printing pattern space
2. 如果沒有下一行,`N`會讓sed在exit前print pattern space一次,忽略後面的動作,沒有下個cycle
::: warning
以上兩種說法,還每有找到範例驗證誰對誰錯,兩種用於解釋碰過的情況都通,但把`-n`啟用,兩種說法的print都會消失
我偏向說法2. 摘自[sed gnu manual](https://www.gnu.org/software/sed/manual/sed.html#Branching-and-Cycles):
```
When the program terminates,
the end-of-cycle actions are performed,
nd the entire pattern space is printed.
```
:::
3. `=` 印出sed的current input line在文本的第幾行,印出行數後再印個newline character(`\n`)換行
``` bash
echo $'hello\nhel\nlo\nhello' | sed '2{N;=;s/\n//}'
```
- output:
```=
hello
3
hello
hello
```
``` bash
echo $'abc\ndef\nghi\njkl\nmno' | sed '=;N;=;l'
```
- output
```=
1
2
abc\ndef$
abc
def
3
4
ghi\njkl$
ghi
jkl
5
mno
```
- 第1行output來自第一個`=`,`abc`被放進pattern space. 因為`N`加入newline(`\n`)到pattern space,並讀取下行input line進pattern space,最後pattern space變成: `abc\ndef`
- 第2行output來自第二個`=`,`N`命令已結束,停在第二行
- 第3行output來自`l`,印出pattern space(unambiguous way)
- 第4,5行因為沒有關掉auto-print(即啟用`-n`)所以一次處理完兩行的pattern space被印出來
- 第6~10行:(原理同上)
- 第11行output來自第一個`=`,`mno`被放進pattern space. 因為已經沒有下一行可以一次處裡兩行了,`N`直接讓sed exit,後面的動作都忽略
- 第12行是因為沒有關掉auto-print(即啟用`-n`),所以只有`mno`的pattern space被印出來
- 如果有啟用`-n`
``` bash
echo $'abc\ndef\nghi\njkl\nmno' | sed -n '=;N;=;l'
```
```=
1
2
abc\ndef$
3
4
ghi\njkl$
5
```
## 處理多行技巧 : `D`,`N`
1. `D`
- 如果當前的pattern space有newline character(即`\n`),則會從pattern space的頭刪除字元至第1個newline charater為止,之後**重啟當前所在的cycle(接續的commands直接忽略),但不讀取新的input line放進pattern space(所以如果用`=`debug,會發現在同一行),新cycle初始的pattern space就是剩餘的字元**,接著就跟`d`一樣,舊cycle的pattern space直接刪除,且抑制auto-printing
- 如果當前的pattern space沒有newline character(即`\n`),則就像`d`一樣,刪除當前pattern space內容,直接重啟cycle,更沒有auto-printing
4. `N` 如果有下一行可以input,則附加newline character(\n)進pattern space後,再把下一行input line也附加進pattern space,動作結束. 如果這時候有=動作,位置會是在下一行的行數; 反之,如果沒有下一行能當input line,則N會讓sed直接exit,也不管後面有什麼動作了,也沒有下個cycle了
6. `l` Print the pattern space in an unambiguous form.
7. `=` 印出sed的current input line在文本的第幾行,印出行數後再印個newline character`\n`換行
``` bash
echo $'abc\ndef\nghi\njkl\nmno' | sed '=;N;=;l;D'
```
- output:
```=
1
2
abc\ndef$
2
3
def\nghi$
3
4
ghi\njkl$
4
5
jkl\nmno$
5
mno
```
- 第1行output來自第1個`=`,`abc`被放進pattern space,`N`開始加入`\n`,再加入`def`進pattern space, pattern space變成`abc\ndef`
- 第2行output來自第2個`=`
- 第3行output來自`l`,印出最沒爭議的pattern space. 之後sed目前在def那行, `D`確認pattern space有`\n`,於是從pattern space頭開始刪除字元到第一個`\n`結束,pattern space變成`def`,並且重啟cycle(在def這行重啟),但不讀取新的input line(即`ghi`),`D`動作結束
- 第4行output來自(上個cycle因D重啟)第1個`=`,輸出sed在第2行, pattern space現在是`def`. `N`判定有下一行可當input line,於是加入`\n`與`ghi`進pattern space
- 第5行output來自這cycle的第2個`=`,`N`已經把第3行`def`放進pattern space, 所以sed停在第3行
- 第6行output來自這cycle的`l`,印出無爭議的pattern space. 目前pattern space是 `def\nghi`, `D`判定pattern space有`\n`,於是從pattern space開頭刪除字元到第1個`\n`停止,pattern space變成`ghi`,重啟cycle(在ghi這行),但不讀取新的input line(即`jkl`),D動作結束
- 第7~12行原理同上,12行`D`結束後,pattern space是`mno`
- 第13行來自被`D`重啟的cycle,第1個`=`顯示sed在第5行. 但是`N`判斷沒有下個input line了,所以cycle直接結束,sed直接exit
- 第14行來自sed exit之前的auto-printing, pattern space裡是`mno`
``` bash
echo $'a\nb\nc' | sed '=;N;=;2D;l'
```
- output:
```=
1
2
2
3
b\nc$
b
c
```
- 第1行output來自於第1個cycle的第1個`=`,此時pattern space裡裝著`a`,碰到第1個cycle的`N`後,pattern space變成`a\nb`
- 第2行output來自於第1個cycle的第2個`=`(因為`N`所以sed在第二行),接著第1個cycle的`2D`(sed在第二行符合條件),刪到pattern space變成`b`後重新開始cycle, auto-printing也被取消
- 第3行outptut來自於第2個cycle的第1個`=`,sed還是在第二行,接著第2個cycle的`N`發現還有下一行可以讀取,pattern space變成`b\nc`
- 第4行outptut來自於第2個cycle的第2個`=`(因為`N`sed在第三行)
- 第5行outptut來自於第2個cycle的`l`,印出當前pattern space,之後就沒有新的一行能讀了,sed結束
- 第6~7行是第2個cycle結束後的auto-printing
## [處理多行技巧: `H` 為分段加入段首,段尾](https://hackmd.io/@3oatvhDfTSqijOwo0tingw/ryGAHIZ__)
## flow control: `b` 直接跳到tag,並執行後續動作
1. `b` branch unconditionally (that is: always jump to a label, skipping or repeating other commands, without restarting a new cycle). Combined with an address, the branch can be conditionally executed on matched lines.
``` bash
echo $'a1\na2\na3' | sed -E '/2/bx; s/a/z/; :x; y/123/456/'
```
```
z4
a5
z6
```
- sed執行的方向是左到右,有符合address才執行command. `/2/bx`告訴sed當碰上該行有`2`則跳至`:x`. 此例中,在第二航剛好跳過一次`s///`. 而`y///`對每一行都有執行
## flow control: `n`與`N`的不同
``` bash
echo $'a\nb\nc' | sed ':x; n; = ;bx'
```
- output:
```=
a
2
b
3
c
```
- 第1行output來自第1個cycle,`n`的print,接著`n`用下一行文字取代pattern space內文,pattern space變成`b`
- 第2行output來自第1個cycle的`=`,因為`n`,所以sed停在第二行;接著透過x標記又回到`n`,cycle沒有結束,沒有讀取下個input line
- 第3行output來自第1個cycle的`n`印出當前pattern space,接著`n`用下一行文字取代pattern space內文,pattern space變成`c`
- 第4行output來自第1個cycle的`=`,因為`n`,所以sed停在第三行;接著透過x標記又回到`n`,cycle沒有結束,沒有讀取下個input line
- 第5行output來自第1個cycle的`n`,因為沒有下個input line了,`n`直接讓sed結束. 最後auto-printing pattern space
``` bash
echo $'a\nb\nc' | sed ':x; N; = ;bx'
```
- output:
```=
2
3
a
b
c
```
- 第1行output:
- 第1個cycle,pattern space裡裝著`a`,碰上`N`,pattern space變成`a\nb`
- 第1個cycle,接著`=`,因為`N`,sed在第二行. 接著碰上`b`而回到`N`
- 第2行output:
- 第1個cycle,pattern space裡裝著`a\nb`,碰上`N`,pattern space變成`a\nb\nc`
- 第1個cycle,接著`=`,因為`N`,sed在第三行. 接著碰上`b`而回到`N`
- 第3~5行output:
- 第1個cycle,pattern space裡裝著`a\nb\nc`,碰上`N`,因為沒有input line了,`N`讓sed直接離開
- 最後auto-printing pattern space
# [好用的 s command](https://hackmd.io/@3oatvhDfTSqijOwo0tingw/rkWyYe1Tp)
# [Multiple commands and their examples](https://hackmd.io/@3oatvhDfTSqijOwo0tingw/rJUqilJ6p)
# Todo:
1. `a`,`r`,`c`,`i`,`w`
1. a r i c w command應該是跟pattern space, hold space與auto-printing 完全無關,屬於跟 `=`與`l`同類的工具,只是`=`,`l`用來debug,上述用來加字
2. [在 sed 中使用 shell variable](https://askubuntu.com/questions/76808/how-do-i-use-variables-in-a-sed-command)