我有一个 bash 函数,它接受一个文件作为参数,验证该文件是否存在,然后将来自 stdin 的任何内容写入该文件。这个简单的解决方案适用于文本,但我在处理任意二进制数据时遇到问题。
echo -n '' >| "$file" #Truncate the file
while read lines
do # Is there a better way to do this? I would like one...
echo $lines >> "$file"
done
答案1
$IFS
您的方法是在用于分割读取的任何分隔符 ( ) 的空间中写入的每个内容中添加换行符。不要将其分解为换行符,而是将整个内容传递出去。您可以将上面的整个代码简化为:
cat - > $file
您不需要截断位,这将截断并将整个 STDIN 流写入其中。
编辑:如果您使用的是 zsh,则可以> $file
代替 cat 来使用。您正在重定向到一个文件并截断它,但是如果有任何东西挂在那里等待接受 STDIN,那么它将在此时被读取。我认为你可以用 bash 做这样的事情,但你必须设置一些特殊的模式。
答案2
要按字面意思读取文本文件,请勿使用 plain read
,它以两种方式处理输出:
read
解释\
为转义字符;用于read -r
关闭此功能。read
在 ; 中的字符上拆分成单词$IFS
;设置IFS
为空字符串以将其关闭。
逐行处理文本文件的常用习惯用法是
while IFS= read -r line; do …
关于这个习语的解释,请参见为什么while IFS= read
如此频繁地使用而不是IFS=; while read..
?。
要按字面意思编写字符串,不要只使用 plain echo
,它以两种方式处理字符串:
- 在某些 shell 上,
echo
进程反斜杠转义。 (在 bash 上,这取决于是否xpg_echo
设置了该选项。) - 一些字符串被视为选项,例如
-n
or-e
(确切的设置取决于 shell)。
从字面上打印字符串的一种便携式方法是使用printf
. (bash 中没有更好的方法,除非您知道您的输入看起来不像 的选项echo
。)使用第一种形式来打印确切的字符串,如果您想添加换行符,则使用第二种形式。
printf %s "$line"
printf '%s\n' "$line"
这个只适合加工文本, 因为:
- 大多数 shell 都会因输入中的空字符而阻塞。
- 当你读完最后一行时,你无法知道末尾是否有换行符。 (如果输入不以换行符结尾,一些较旧的 shell 可能会遇到更大的问题。)
您无法在 shell 中处理二进制数据,但大多数 unice 上的实用程序的现代版本可以处理任意数据。要将所有输入传递到输出,请使用cat
.偏离正题echo -n ''
是一种复杂且不可移植的无所事事的方式。echo -n
会一样好(或不取决于外壳),并且:
更简单且完全便携。
: >| "$file"
cat >>"$file"
或者,更简单的是,
cat >|"$file"
在脚本中,您通常不需要使用>|
sincenoclobber
默认情况下处于关闭状态。
答案3
这将完全满足您的要求:
( while read -r -d '' ; do
printf %s'\0' "${REPLY}" ;
done ;
# When read hits EOF, it returns non-zero which exits the while loop.
# That data still needs to be output:
printf %s "${REPLY}"
) >> ${file}
但请注意内存使用情况。这以空分隔的方式读取输入。
如果没有\0
无效的如果输入中包含字节,则 bash 首先需要将输入的全部内容读取到内存中,然后将其输出。
关于您的截断步骤:
echo -n '' >| "$file" #Truncate the file
一个更简单且等效的方法是:
> ${file} #Truncate the file