我遇到了问题,我需要复制文件内容并删除几行(如果它们与上一个命令的输出匹配)。但到目前为止,我在保持文件行完全相同方面遇到了问题。我将脚本的简单部分作为 if 省略副本不是问题的一部分,因为这种情况发生在未受影响的行上。
例如:
在原始文件中我有以下内容
Testing, resuming text
运行脚本时,字段变为:
Testing, resuming text
我正在执行以下操作:
#!/usr/bin/bash
rm /tmp/dest_file
while read line
do
echo $line >> /tmp/dest_file
done < $1
我遇到的问题是,由于制表符格式的字段,文件会变得不同。
答案1
这个网站已经出现过好几次了——参见了解 IFS以及相关问题。在这个答案中,我将总结什么可能会出错以及如何避免它;有关详细信息,请参阅链接的线程。
read line
执行以下操作:
- 从标准输入读取第一个字节(换行符或空值),并将数据放入名为 的变量中
line
。 - 去掉不在行尾的任何反斜杠。双反斜杠
\\
变成单反斜杠。换句话说,反斜杠引用下一个字符,只要它不是换行符。 - 如果
read
停在换行符处并且该行末尾的字符是 a\
,则去除反斜杠换行符序列并继续读取,附加到变量line
。重复直到第一个: 前面没有反斜杠的换行符;一个空字节;输入结束。 line
去掉由 中的字符组成的最长后缀$IFS
。默认情况下,IFS
包含一个制表符、一个空格和一个换行符,因此这会从 值的末尾去除 ASCII 空格line
。line
去掉中由空格字符组成的最长前缀$IFS
。
例如,如果输入是
: hello\
world: :
wibble
然后read line
导致line
包含: helloworld: :
(无初始空间),默认值为IFS
。如果IFS
已更改为:
(仅一个冒号),则read line
结果为 : helloworld:
(开头和结尾都有一个空格)。如果IFS
同时包含:
和 一个空格,则结果为: helloworld
(无初始或尾随空格)。
为了避免 的影响IFS
,请将其设置为空值(注意这与取消设置不同)。您只能read
通过编写命令来设置它IFS= read
(请参阅为什么如此频繁地使用“while IFS= read”,而不是“IFS=;”在阅读时..`?)。
为了避免反斜杠处理,请将-r
选项传递给read
.
除非 shell 是 zsh,否则如果输入中存在空字节,则后续字符将丢失。 Shell 并非设计用于读取二进制数据。
因此,一次读取一行的习惯用法是:
while IFS= read -r line; do
… # process "$line"
end
当您使用变量时line
,请确保总是在变量替换两边加上双引号: "$line"
。如果没有双引号,shell 首先扩展变量的值,然后在包含来自 的字符的地方将该值分解为单独的单词IFS
,并且每个单词都被解释为通配符模式并替换为匹配文件的列表(如果没有匹配文件,模式保持原样)。因此扩展到当前目录中以或echo 'a* b*' | IFS= read -r line; echo $line
开头的文件列表;要使输入保持不变,请使用.a
b
echo 'a* b*' | IFS= read -r line; echo "$line"
另请注意,该echo
命令有时会修改它打印的字符串。确切的方式取决于 shell。有些 shell 处理反斜杠转义,有些 shell 识别选项。echo
仅当您知道该字符串不包含任何反斜杠且不以破折号 ( -
)开头时,才能逐字输出字符串。一种按原样打印字符串的可靠且便携的方法
printf '%s\n' "$line"
这会在字符串后打印一个换行符,例如echo
.您可以通过\n
在上面的命令中省略来省略换行符。
答案2
引用你的变量:
echo "$line" >> /tmp/dest_file