# 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)