使用 POSIX 工具将换行符替换为字符串 '\n'

replacing newlines with the string '\n' with POSIX tools

是的,我知道有很多问题(例如 (0) or )似乎问的都是一样的,但 AFAICS none 确实回答了我想要的。

我想要的是,用字符串 \n 替换任何出现的换行符 (LF), 没有 隐式假定的换行符... POSIX 仅 实用程序(无 GNU 扩展或 Bashisms)和从标准输入读取的输入 不需要缓冲

例如:

通常给出的答案,不要这样做,例如:

到目前为止,我唯一可行的解​​决方案是使用 perl:
perl -p -e 's/\n/\n/'
它在所有情况下都按预期工作,但正如我所说,我想为只有基本 POSIX 实用程序的环境提供解决方案(因此没有 Perl 或使用任何 GNU 扩展)。

提前致谢。

这是一个 tr + sed 解决方案,应该适用于任何 POSIX shell,因为它不调用任何 gnu 实用程序:

printf 'foo' | tr '\n' '' | sed 's/\x7/\n/g'
foo

printf 'foo\n' | tr '\n' '' | sed 's/\x7/\n/g'
foo\n

printf 'foo\n\n' | tr '\n' '' | sed 's/\x7/\n/g'
foo\n\n

详情:

  • tr 命令将每个换行符替换为 \x07
  • sed 命令将每个 \x07 替换为 \n

以下内容将适用于所使用工具的所有 POSIX 版本以及任何 POSIX 文本允许字符作为输入,无论是否存在终止换行符:

$ magic() { { cat -u; printf '\n'; } | awk -v ORS= '{print sep [=10=]; sep="\n"}'; }

$ printf 'foo' | magic
foo$

$ printf 'foo\n' | magic
foo\n$

$ printf 'foo\n\n' | magic
foo\n\n$

该函数首先向传入的管道数据添加换行符,以确保 awk 正在读取的是有效的 POSIX 文本文件(必须 以换行符结尾) 所以它保证在所有 POSIX 兼容的 awks 中工作,然后 awk 命令丢弃我们添加的终止换行符,并根据需要用 "\n" 替换所有其他的换行符。

上面唯一一个必须在没有终止换行符的情况下处理输入的实用程序是 cat,但是 POSIX 只是谈论“文件”作为 cat, not "text files" as in the awk and sed 规范的输入,所以每个POSIX 兼容版本的 cat 可以处理没有终止换行符的输入。

你可以(我认为)用纯 POSIX shell 来做到这一点。我假设您正在处理文本,而不是可以包含空字节的任意二进制数据。

magic () {
  while read x; do
      printf '%s\n' "$x"
  done
  printf '%s' "$x"
}

read 假定 POSIX 文本行(以换行符结尾),但它仍然会填充 x 它读取的任何内容,直到输入结束时没有看到换行符。因此,只要 read 成功,您就可以在 x 中有一个正确的行(减去换行符),您可以写回,但使用文字 \n 而不是换行符。

一旦循环中断,在失败 read 后输出 x 中的任何内容(如果有的话),但 没有 尾随文字 \n.

$ [ "$(printf foo | magic)" = foo ] && echo passed
passed
$ [ "$(printf 'foo\n' | magic)" = 'foo\n' ] && echo passed
passed
$ [ "$(printf 'foo\n\n' | magic)" = 'foo\n\n' ] && echo passed
passed