干货!sed 中的疑难杂症
链接:https://www.cnblogs.com/f-ck-need-u/p/7499309.html
1.sed中使用变量和变量替换的问题
total=`wc -l <a.txt`
sed -n '$((total-4)),$p' a.txt
$(())
中也出现了"$"符号,这会让sed去解析该符号。另一方面,$(())
这部分是使用shell计算而不是使用sed计算的,因此必须要将其暴露给shell,以便能让shell能解析它。单引号:单引号内的所有字符变为字面符号。但注意:单引号内不能再使用单引号,即使使用了反斜线转义也不允许。 双引号:双引号内的所有字符变为字面符号,但"\"、"$"、"`"(反引号)除外,如果开启了"!"引用历史命令时,则感叹号也除外。 不使用引号:几乎等同于使用了双引号,但会进行大括号和波浪号扩展。
sed '$d' filename
sed '1!d' filename
sed -n '2{p;q}' filename
$(())
是想被shell解析的,因此必须使用单引号或者不加引号将其暴露给shell。所以正确的语句是:sed -n $((total-4))',$p' a.txt
sed -n "$((total-4))"',$p' a.txt
sed -n "$((total-4)),\$p" a.txt
遇到需要被shell解析的都不加引号,或者加双引号; 遇到shell和所执行命令共有的特殊字符时,要想被sed解析,必须加单引号,或者在双引号在加反斜线转义; 那些无关紧要的字符,无论加什么引号。
sed -n `expr $(wc -l <a.txt) - 4`',$p' a.txt
`expr $(wc -l <a.txt) - 4`
要被shell解析,因此必须不能使用单引号包围。而$p
部分的"$"要被sed解析成最后一行,必须使用单引号以避免被shell解析。str="abc"
sed -n /^$str/',$p' a.txt
$str
能如期被shell替换成"abc"。这个命令还有多种写法:sed -n '/^'$str'/,$p' a.txt
sed -n "/^$str"'/,$p' a.txt
sed -n "/^$str/,\$p" a.txt
sed -n "/^$str/,"'$'p a.txt
[root@xuexi ~]# tail -n 1 /etc/shadow
userX:$6$hS4yqJu7WQfGlk0M$Xj/SCS5z4BWSZKN0raNncu6VMuWdUVbDScMYxOgB7mXUj./dXJN0zADAXQUMg0CuWVRyZUu6npPLWoyv8eXPA.::0:99999:7:::
old_pass="$(tail -n 1 /etc/shadow | cut -d':' -f2)"
new_pass='$1$123456$wOSEtcyiP2N/IfIl15W6Z0'
sed -n '$'s%$old_pass%$new_pass%p /etc/shadow
old_pass
和old_pass
中包含了"/"和"$"符号,因此"s"命令的分隔符使用了"%"替代。再仔细观察new_pass,其内有"."符号,这是正则表达式的元字符,因此它还可以匹配其他情况。2.反向引用失效问题
(a)\1u|b\1
将只匹配"aau"的行,不匹配"ba"的行,因为在二者选一的第二个正则中\1
代表的分组没有参与匹配,所以第二个正则中的\1
失效,但是第一个正则中的\1
有效。echo "ab3456cd" | sed -r "/(ab)/s/([0-9]+)/\1/"
\2
引用,则会报错"invalid reference \2 on 's' command's RHS"。3."-i"选项的文件保存问题
4.贪婪匹配问题
echo "abcdbaz" | grep -o "a.*b"
abcdb
echo "abcdbaz" | grep -P -o "a.*?b"
ab
echo "abcdbaz" | grep -o "a[^b]*b"
ab
rootx:0:0:root:/root:/bin/bash
sed -r 's/^([^:]*):.*/hello \1/' /etc/passwd
sed -r 's/^([^:]*):([^:]*):.*/hello \1 \2/' /etc/passwd
sed -r 's/^([^:]*:){2}([^:]*):.*/hello \2/' /etc/passwd
sed -r 's/^([^:]*:){2}([^:]*):([^:]*):([^:]*):/hello \2 \4/' /etc/passwd
sed -r 's/^([^:]*:){2}(([^:]*:){3}).*/hello \2/' /etc/passwd
5.sed命令"a"和"N"的纠葛
echo -e "aaa\nbbb\nccc\nddd" | sed '/ccc/a matched successful'
aaa
bbb
ccc
matched successful
ddd
echo -e "aaa\nbbb\nccc\nddd" | sed '/ccc/{a\
matched successful
;N}'
aaa
bbb
matched successful
ccc
ddd
if [ "$line" -ne "$last_line_num" ];then
lock pattern_space;
auto_print;
remove_pattern_space;
unlock pattern_space;
append "\n" to pattern_space;
read next_line to pattern_space;
else
auto_print;
remove_pattern_space;
exit;
fi
回到"a"命令和"N"命令结合的问题上。之所以"a"命令的队列化文本会插入在匹配行的前面,问题就出在输出空流上。"N"在准备读取下一行时,它有输出动作,即使输出结果为空。而"a"命令是时刻等待sed输出流的,只要一有输出流,立马就会追上去追加在输出流的屁股后面。因此,"matched successful"会追加在空流的尾部,追加之后"N"才会读入下一行,最后输出模式空间中的内容"ccc\nddd",也就得到前面"有悖期待"的结果。
6.sed中感叹号取反的弯弯绕绕
你知道使用"!"号取反,但也许你并没有发现感叹号可以放在定址表达式后,也可以放在命令的前面。这两者虽然都是取反,但意义决然不同,最终导致的结果也不同。
感叹号在定址表达式后,表示对行进行筛选。表示满足条件的行不执行命令,但不满足的行会执行。
感叹号在命令的前面,表示满足条件的行不执行该命令,且不满足的行也不执行(不执行是因为没有被定址表达式匹配到)。这是对模式空间中的行筛选要执行的命令。
假如文件a.txt中包含了3行:
djkaldahsdf
abcskdf2das
chhdsjaj
对于以下三个sed脚本:
(1).
/^abc/!{d}
(2).
/^abc/{!d}
(3).
/^abc/!d
示例(1)中感叹号放在定址表达式后,这表示不是以字母"abc"开头的行会执行d删除命令。而那些以"abc"开头的行,则不符合定址表达式,后续的d命令不会执行。也就是说,该sed脚本的作用是:除了"abc"开头的行,其余行全删除,因此只输出第2行。
示例(2)中感叹号放在命令的前面,而非定址表达式后面。这表示的是以"abc"开头的行不执行d命令。而那些不以"abc"开头的行由于不满足定址条件,也不会执行d命令。也就是说,该sed脚本的d命令是多余的,任何行都不会删除。因此所有行都输出。
示例(3)等价于示例(1),因为定址匹配动作优先于命令的执行,感叹号直接被认为是定址表达式的一部分。
但不管哪种情况,对于不满足定址表达式的(定址后的感叹号也算是定址表达式的一部分)行,都不会执行后续任何命令,这些行是直接自动输出的,由"-n"选项控制是否将其输出。
7.sed卡死,cpu 100%问题
有些人可能遇到过这种问题,特别是sed处理以UTF-8格式导出的数据库文件。
之所以会出现这样的问题,是因为字符集的问题,确切地说是本地环境(locale)和文件的编码不一致。
如果出现这样的问题,可以将LC_COLLATE和LC_CTYPE环境变量设置为C。也可以简单地设置LANG=C或LC_ALL=C。
END
官方站点:www.linuxprobe.com
Linux命令大全:www.linuxcool.com
刘遄老师QQ:5604215
Linux技术交流群:2636170
(新群,火热加群中……)
想要学习Linux系统的读者可以点击"阅读原文"按钮来了解书籍《Linux就该这么学》,同时也非常适合专业的运维人员阅读,成为辅助您工作的高价值工具书!
微信扫码关注该文公众号作者