sed

最後更新: 2019-12-10

介紹

Sed (stream editor)

很詳細的 Documment: http://www.grymoire.com/Unix/Sed.html

目錄

  1. 常用 options
  2. Command
  3. 刪除()
  4. 取代(substitution)
    Use variable in replacement
  5. "|" ":" "@"
  6. print (簡寫 p)
  7. Comments
  8. Print line number with =
  9. Transform with y (1對1的)
  10. Grouping with { and }
  11. Adding(a), Inserting(i), Changing lines
  12. How sed Works
  13. Writing a file with the 'w' command
  14. Change configuration files setting value (ini file)

 


常用 options

 

sed [OPTION]... {script-only-if-no-other-script} [input-file]...

# version

sed --version

GNU sed version 4.2.1

-n, --quiet ( 在 print(p) 時只會 output 中 pattern 的 line )

-e 'script'               # 可略

echo -e '1\n2\n3\n4\n5' > test.txt
sed '1d' test.txt               # 刪除第1行

-f script-file

echo 1d > cmd.sed
sed -f cmd.sed test.txt         # 刪除第1行

修改 File

當沒有 -i 或 -c 時, 只會在 stdout 輸出改過後的內容

# edit files in place (makes backup if extension supplied)

-i[SUFFIX], --in-place=[SUFFIX]

# use copy instead of rename when shuffling files in -i mode.

-c, --copy

# Use extended regular expressions in the script.

-r, --regexp-extended

 


Addresses

 

Syntax

addr1,addr2[!]

no addresses

in which case the command will be executed for all input lines;

one address

command will only be  executed  for input lines which match that address

two addresses       # address-range

starting  from the first address and continuing to the second address

 


Silent mode(-n)

 

cat test.txt

first
second
third

Prints only lines 2 through 3, as requested

sed -n '2,3p' test.txt

second
third

Prints each line (automatically), AND ALSO prints lines 2-3 a second time

By default, sed will print out the pattern space at the end of each cycle through the script.

These options(-n) disable this automatic printing,
  and sed will only produce output when explicitly told to via the p command.

 

sed '2,3p' test.txt

first
second
second
third
third

"-n" 在 "s/" 情況

replaces "t" with "T" on each line

sed 's/t/T/' test.txt          # automatically prints the result

sed -n 's/t/T/' test.txt      # doesn't print the result due to -n

 

 


Command

 

  • s/regexp/replacement/         <--- 用得最多
  • p
  • d
  • w filename
  • y
  • =

 


Delimiter

 

/ _ : | @

 

一切為了簡化

將 path.txt 的內容由 /opt/name/bin 變成 /opt/tools/bin

sed 's/\/opt\/name\/bin/\/opt\/tools\/bin/g' path.txt

應用:

sed 's@/opt/name/bin@/opt/tools/bin@g' path.txt

 


實際使用

 

刪除

建立 test.txt

echo -e '1\n2\n\n4\n5' > test.txt

 

刪除第N行

# 刪除第2行

sed '2d' /etc/services

 

 * 配合 "-i" 可原地 replace

# 刪除第2至第4行

sed '2,4d' test.txt

# 只保留第 2~4 行, 刪除其他

sed '2,4!d' test.txt

 

Remove line "matched" by a regex

# 刪除有英文字"awk" 的所有行

sed '/awk/d' file.txt >  file-new.txt

# Remove all empty lines:

sed '/^$/d' filename.txt    

i.e.

mv redis.conf redis.conf.bak

grep -v '#' redis.conf.bak > redis.conf

sed -i '/^$/d' redis.conf

 


取代(substitution)

 

把在 testfile 檔內的每行 is 取代 成 are, 然後 output 到 stdout

sed 's/is/are/g' testfile

sed 's#is#are#g' testfile

Remark

 - The s stands for substitute,

 - the g stands for global. which means that all matching occurrences in the line would be replaced

    有些版本的 sed 是不用加 /g 的也是 global replacement

直接 replace file

sed -i 's/is/are/g' testfile

Interesting usage insert / remove "#"

# insert "#" at the beginning of a line

sed -i '3 s/^/#/' test.txt

# remove "#" at the beginning of a line

sed -i '3 s/^#//' test.txt

Add string to each line in file

sed -e 's/$/string after each line/' -i filename

刪除 "\n"

情況

cat -A tmp.txt

CH1: OFF$
$
CH2: OFF$
$

sed 's#\n\n#\n#g'    # replace 失敗 !!

原因

The "\n" will never match the newline at the end-of-line

  because the newline is always stripped off before the line is placed into the pattern space.

解決方法

-z            separate lines by NUL characters

sed -z 's#\n\n#\n#g'

刪除行首開始的 3 粒字

echo 123456 | sed 's%^.\{3\}%%g'

Use variable in replacement

RepoFile="CentOS-Base.repo"

BASEURL="https://repo.hlhk.net:10443/centos/centos6"

# 使用 (") 去引入 variable 及 (\$) 去 excape 其他 $

sed -i "\@^#baseurl=http.*/contrib/.*@a baseurl=$BASEURL/\$releasever/contrib/\$basearch/" $RepoFile

 

 


Regular Expression

 

Position

^    Matches the beginning of the line
$    Matches the end of the line

/^#/            Will match any line that begins with a '#'
/^$/            Will match all blank lines
/}$/            Will match any lines that ends with '}' (no spaces)
/} *$/          Will match any line ending with '}' followed by zero or more spaces

數量

  • *                                   # match zero or more occurrences of the previous character
  • \+                                 # matches one or more
  • \?                                  # matches zero or one.
  • \{i\}   \{i,j\}   \{i,\}       # i 個; i~j 個; i 個或以上

字符

.    Matches any single character <--- 真正的"." 是 "\."

/./             Will match any line that contains at least one character
/../            Will match any line that contains at least two characters

[]   Matches all the characters inside the [ ]

/[abc]/         Will match any line that contains a lowercase 'a', 'b', or 'c'
/^[abc]/        Will match any line that begins with an 'a', 'b', or 'c'

Ampersand (&):

"&"                 # 相當於命中了 /pattern/ 的部分

# Output: a123456

echo "a123" | sed 's/.*/&456/'

# Output: a456123

echo "a123" | sed 's/a/&456/'

# Output: 123 123 abc

echo "123 abc" | sed 's/[0-9]*/& &/'

 

# 用 sed 扮 grep

sed -n -e's/YourPattern/&/p' < file.txt

(pattern)

\1  through  \9  to  refer to the corresponding matching pattern

# "-n" option which tells sed not to print
# "p" flag which tells sed to print what is matched

ls -og /sys/block/sd*

lrwxrwxrwx 1 0 May 17 19:24 /sys/block/sda ->
 ../devices/pci0000:00/0000:00:11.0/ata1/host0/target0:0:0/0:0:0:0/block/sda

ls -l /sys/block/sd* | sed -n 's/.*\(sd.\) -.*\(ata.*\)\/h.*/\2 => \1/p'

 


print (簡寫 p)

 

/p - print

 

sed -n -e '/regexp/p' /path/to/my/test/file | more

  • -n 只print 有修改的行

By default, sed prints every line. If it makes a substitution, the new text is printed instead of the old one.

 

找出檔案在 N 與 M之間的內容(N,M 可以是數字式 patten):

usage:

sed -n -e'N,Mp' file.txt

i.e.

sed -n -e'6,7p' test.txt

sed -n '/begin/,/end/p' test.txt

 


Comment

 

Sed comments are lines where the first non-white character is a "#."

On many systems, sed can have only one comment, and it must be the first line of the script.

 


Print line number with =

 

sed -n '/PATTERN/ =' file

 


Transform with y (1對1對換)

 

test.txt

aaaaaa11111
bbbbbb22222
cccccc33333
dddddd44444

sed 'y/bbbbbb/eeeeee/' test.txt

aaaaaa11111
eeeeee22222
cccccc33333
dddddd44444

b 與  e 必須一樣長度
 


Multiple commands for one address(Grouping with { and })

 

Sometimes, you may want to specify multiple commands that will apply to a single address.

This comes in especially handy when you are performing lots of 's///' to transform words or syntax in the source file.

To perform multiple commands per address, enter your sed commands in a file, and use the '{ }' characters to group commands, as follows:

i.e.

1,20{
    s/[Ll]inux/GNU\/Linux/g
    s/samba/Samba/g
    s/posix/POSIX/g
}

i.e.

1,/^END/{
    s/[Ll]inux/GNU\/Linux/g
    s/samba/Samba/g
    s/posix/POSIX/g
    p
}

This example will apply all the commands between '{ }' to the lines starting at 1 and up to a line beginning with the letters "END",

or the end of file if "END" is not found in the source file.

i.e.

#!/bin/sh
# This is a Bourne shell script that removes #-type comments
# between 'begin' and 'end' words.
sed -n '
    /begin/,/end/ {
         s/#.*//
         s/[ ^I]*$//
         /^$/ d
         p
    }
'

 


Append(a|r), Inserting(i), Changing(c) lines

 

Append a line with 'a'

Example 1: sed append to end of file

echo 'line 1' > test.txt

cat -A test.txt

line 1$

sed 'a line 2' test.txt                   # 相當於 echo 'line 2' >> test.txt

line 1
line 2

Example  2: Appends a line after the range or pattern.

This example will add a line after every line with "WORD"

#!/bin/sh
sed '
/WORD/ a\
Add this line after every line with WORD
'

 * lines are continued by ending the previous line with a "\"

Append a file

r filename             # Append text read from filename.

Insert a line with 'i'

You can insert a new line before the pattern with the "i" command:

#!/bin/sh
sed '
/WORD/ i\
Add this line before every line with WORD
'

在 match 的行的行頭 insert "#"

Usage:

sed /match/s/^/#/ filename

i.e.

sed /authn_dbd_module/s/^/#/ 00-base.conf

Change a line with 'c'

You can change the current line with a new line.

#!/bin/sh
sed '
/WORD/ c\
Replace the current line with the line
'

i.e.

cat myfile

aaaaaa11111
bbbbbb22222
cccccc33333
dddddd44444

sed '/bbbbbb/c eeeee' myfile

cat myfile

aaaaaa11111
eeeee
cccccc33333
dddddd44444

A "d" command followed by a "a" command won't work, as I discussed earlier.

The "d" command would terminate the current actions. You can combine all three actions using curly braces:

#!/bin/sh
sed '
/WORD/ {
i\
Add this line before
a\
Add this line after
c\
Change the line to this one
}'

 


How sed Works

 

Actually, if sed prints a line without the terminating newline, it will nevertheless print the missing newline as soon as more text is sent to the same output stream, which gives the “least expected surprise” even though it does not make commands like ‘sed -n p’ exactly identical to cat.

sed maintains two data buffers: the active pattern space, and the auxiliary hold space. Both are initially empty.

sed operates by performing the following cycle on each line of input: first, sed reads one line from the input stream, removes any trailing newline, and places it in the pattern space. Then commands are executed;

When the end of the script is reached, unless the -n option is in use, the contents of pattern space are printed out to the output stream, adding back the trailing newline if it was removed.

Then the next cycle starts for the next input line.

 


Pattern Space & Hold Space

 

Pattern Space:

The pattern space is the buffer where sed reads and processes each line of input.

It is a temporary buffer where sed reads and performs operations on the input text.

Hold Space:

The hold space is an additional buffer that can be used to store content temporarily.

It is not affected by the commands executed on the pattern space.

The hold space is useful when you need to perform operations that involve multiple lines or require storing data for later use.

 


Multiline techniques

 

D,G,H,N,P are similar to their lowercase counterparts (d,g, h,n,p),
except that these commands append or subtract data while respecting embedded newlines

To transfer data between the pattern space and the hold space,
you can use the h and g commands.
h copies the pattern space to the hold space, while g copies the hold space to the pattern space.

N

Appends line from the input file to the pattern space.

When sed reads a line of input, it stores it in the pattern space.

By default,sed processes one line at a time,
reading the input line, performing any specified operations on it,
and then outputting the modified line.

i.e.

file.txt content

=============
@line1 ABCDE@
@line2 FGHIJ@
@line3 KLMNO@
=============

# N appends the next line to the pattern space.
# By using N multiple times, we can append multiple lines to the pattern space.

sed -n '/ABCDE/{N;N;p;}' file.txt

H

appends line from the pattern space to the hold space, with a newline before it.

 

G

appends line from the hold space to the pattern space, with a newline before it.

D

deletes line from the pattern space until the first newline, and restarts the cycle.

P

prints line from the pattern space until the first newline.

Example

# h, H, G

seq 6 | sed '1h; 2,4H; 5G'

1
2
3
4
5
1
2
3
4
6

#

seq 6 | sed -n 'N;l;D'

The l command prints the content of the pattern space unambiguously ("\n")

The D command then removes the content of pattern space up to the first newline

(leaving ‘2’ at the end of the cycle).

 


進階 Search & Replace

 

g

Apply the replacement to all matches to the regexp, not just the first.

number

Only replace the numberth match of the regexp.

p

If the substitution was made, then print the new pattern space.

m

The m modifier to regular-expression matching is a GNU sed extension
 which directs GNU sed to match the regular expression in multi-line mode.
There are special character sequences (\` and \') which always match the beginning or the end of the buffer.
In addition, the period character does not match a new-line character in multi-line mode.

# 解決 ^ 不是每行的情況

sed '/ABCDE/{N;N;s/^@//g;}' file.txt

 line1 ABCDE
@ line2 FGHIJ
@ line3 KLMNO

w filename

If the substitution was made, then write out the result to the named file.
As a GNU sed extension, two special values of filename are supported: /dev/stderr.

 

 


BRE & ERE

 

In GNU sed, the only difference between basic and extended regular expressions
is in the behavior of a few special characters: ‘?’, ‘+’, parentheses, braces (‘{}’), and ‘|’.

With basic (BRE) syntax, these characters do not have special meaning unless prefixed with a backslash (‘\’);
While with extended (ERE) syntax it is reversed: these characters are special unless they are prefixed with backslash (‘\’).

Example

BRE(Default)

echo 'a+b=c' | sed -n '/a+b/p'

ERE("-E")

echo 'a+b=c' | sed -E -n '/a\+b/p'

 

 

 

 


Writing a file with the 'w' command

 

sed -n 's/^[0-9]*[02468] /&/w even' < file

I used the "&" in the replacement part of the substitution command so that the line would not be changed.

A simpler example is to use the "w" command, which has the same syntax as the "w" flag in the substitute command

 


Change configuration files setting value (ini file)

 

cat test.ini

[Default]
setting1=false
setting2=false

方法1: 直觀的 search & replace

# -r  regex

replaceValue=true
sed -i -r "s/^setting1=.*/setting1=$replaceValue/" test.ini

方法2: 行 script

# 在中 "^setting1=" 那行上將 "=" 及其後的字取代成 "=true"

replaceValue=true
sed -i -e "/^setting1=/ s/=.*/=$replaceValue/" test.ini

方法3: 不建議, 因為有機會有多個 Section

sed -i "/^setting2=/d" test.ini
echo "setting2=$replaceValue" >> test.ini

Scripts

set_config.sh

#!/bin/bash

set_config(){
        if [ -f $1 ]; then
                sed -i -r "s@^$2\s*=.*@$2=$3@" $1
        else
                echo "No file $1"
        fi
}

set_config php.ini short_open_tag On
set_config php.ini expose_php Off
set_config php.ini memory_limit 256M
set_config php.ini max_execution_time 60
set_config php.ini session.gc_maxlifetime 3600
set_config php.ini upload_max_filesize 80M
set_config php.ini post_max_size 100M

 


 

Creative Commons license icon Creative Commons license icon