使用 POSIX 工具将换行符替换为字符串 '\n'
replacing newlines with the string '\n' with POSIX tools
是的,我知道有很多问题(例如 (0) or )似乎问的都是一样的,但 AFAICS none 确实回答了我想要的。
我想要的是,用字符串 \n
替换任何出现的换行符 (LF), 没有 隐式假定的换行符... POSIX 仅 实用程序(无 GNU 扩展或 Bashisms)和从标准输入读取的输入 不需要缓冲。
例如:
printf 'foo' | magic
应该给 foo
printf 'foo\n' | magic
应该给 foo\n
printf 'foo\n\n' | magic
应该给 foo\n\n
通常给出的答案,不要这样做,例如:
- awk
printf 'foo' | awk 1 ORS='\n
给出 foo\n
,而它应该只给出 foo
所以在有 no 换行符时添加 \n
。
- sed
仅适用于 foo
但在所有其他情况下,例如:
printf 'foo\n' | sed ':a;N;$!ba;s/\n/\n/g'
给出 foo
,而它应该给出 foo\n
错过最后一个换行符。
因为我不想要任何类型的缓冲,所以我不能只查看输入是否以换行符结尾,然后手动添加缺失的。
无论如何...它将使用 GNU 扩展。
sed -z 's/\n/\n/g'
确实有效(甚至正确保留了 NUL),但同样是 GNU 扩展。
- tr
只能替换一个字符,而我需要两个。
到目前为止,我唯一可行的解决方案是使用 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
是的,我知道有很多问题(例如 (0) or
我想要的是,用字符串 \n
替换任何出现的换行符 (LF), 没有 隐式假定的换行符... POSIX 仅 实用程序(无 GNU 扩展或 Bashisms)和从标准输入读取的输入 不需要缓冲。
例如:
printf 'foo' | magic
应该给foo
printf 'foo\n' | magic
应该给foo\n
printf 'foo\n\n' | magic
应该给foo\n\n
通常给出的答案,不要这样做,例如:
- awk
printf 'foo' | awk 1 ORS='\n
给出foo\n
,而它应该只给出foo
所以在有 no 换行符时添加\n
。 - sed
仅适用于foo
但在所有其他情况下,例如:
printf 'foo\n' | sed ':a;N;$!ba;s/\n/\n/g'
给出foo
,而它应该给出foo\n
错过最后一个换行符。
因为我不想要任何类型的缓冲,所以我不能只查看输入是否以换行符结尾,然后手动添加缺失的。
无论如何...它将使用 GNU 扩展。
sed -z 's/\n/\n/g'
确实有效(甚至正确保留了 NUL),但同样是 GNU 扩展。 - tr
只能替换一个字符,而我需要两个。
到目前为止,我唯一可行的解决方案是使用 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