sed

BSD sed 和 GNU sed 的异同,参考 BSD/macOS Sed vs. GNU Sed vs. the POSIX Sed specification (链接备份)

-i 参数不一致

BSD sed

echo 123 > abc
sed -i '' 's/1/2/' abc # ok
sed -i'' 's/1/2/' abc  # error: 1: "abc": command a expects \ followed by text
sed -i 's/1/2/' abc    # error: 1: "abc": command a expects \ followed by text

GNU sed

echo 123 > abc
sed -i '' 's/1/2/' abc # error: can't read s/1/2/: No such file or directory
sed -i'' 's/1/2/' abc  # ok
sed -i 's/1/2/' abc    # ok

解决方案

只有一种方法同时支持 BSD sed 和 GNU sed,那就是

sed -i.bak 's/1/2/' abc
rm abc.bak

或者提前判断 sed 命令是不是 GNU 的。

# 此方法来自于 https://github.com/adoyle-h/lobash/blob/develop/src/internals/is_gnu_sed.bash
is_gnu_sed() {
  local out
  out=$(${1:-sed} --version 2>/dev/null)
  [ $out =~ 'GNU sed' ]
}

# if is_gnu_sed gsed; then
if is_gnu_sed; then
  echo "is GNU sed"
else
  echo "is BSD sed"
fi

替换换行符无效

官方文档有说明。

5.10. Why can’t I match or delete a newline using the \n escape sequence? Why can’t I match 2 or more lines using \n? 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. To get 2 or more lines into the pattern space, use the ‘N’ command or something similar (such as ‘H;…;g;’).

解决方法

  • GNU sed: sed ':a;N;$!ba;s/\n//g' file
  • BSD sed: sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g' file

颜色字符在 sed 替换中失效

无效写法

  • <<<"red green" sed -E 's/(.+) (.+)/\033[31m\1\033[32m \2\033[0m/'
  • <<<"red green" sed -E 's/(.+) (.+)/\\033[31m\1\\033[32m \2\\033[0m/'
  • <<<"red green" sed -E 's/(.+) (.+)/\e[31m\1\e[32m \2\e[0m/'

颜色字符是通过 ASCII 转义序列 来控制终端的字符颜色显示的。

转义序列是由 ESC + [ + 其他字符组成的序列。其中 ESC 的表示方法有很多,详见这篇文章。 进制表示法和转换见这篇文章

sed 就不识别 \033\e。但是 sed 自 GNU sed v3.02.80 起就支持十六进制,因此可以用十六进制的 \x1b。 所以有效写法是 <<<"red green" sed -E 's/(.+) (.+)/\x1b[31m\1\x1b[32m \2\x1b[0m/'

另外比如 \0 (expect NUL) 在 sed 里也不支持,需要用十六进制的 \x0\x00 表示。