我需要将一个文件读入一个数组,并在每一行的末尾连接一个字符串。这是我的 bash 脚本:
#!/bin/bash
IFS=$'\n' read -d '' -r -a lines < ./file.list
for i in "${lines[@]}"
do
tmp="$i"
tmp="${tmp}stuff"
echo "$tmp"
done
但是,当我这样做时,会发生 replace
操作,而不是连接。
例如,在file.list
中,我们有:
http://www.example1.com
http://www.example2.com
我需要的是:
http://www.example1.comstuff
http://www.example2.comstuff
但是在执行上面的脚本之后,我在终端上得到如下信息:
stuff//www.example1.com
stuff//www.example2.com
顺便说一句,我的电脑是 Mac 操作系统。
通过 awk
、printf
和 echo
命令连接字符串时也会出现此问题。例如 echo $tmp"stuff"
或 echo "${tmp}""stuff"
最佳答案
文件 ./file.lst
很可能是在 Windows 系统上生成的,或者至少是使用 Windows 行尾约定保存的。
Windows 使用两个字符的序列来标记文本文件中行的结尾。这些字符是 CR
(\r
) 后跟 LF
(\n
)。类 Unix 系统(从版本 10 开始的 Linux 和 macOS)使用 LF
作为行尾字符。
代码中 read
前面的赋值 IFS=$'\n'
告诉 read
使用 LF
作为行分隔符。 read
不会将 LF
字符存储在它生成的数组 (lines[]
) 中,而是从 lines[]
以 CR
字符结尾。
tmp="${tmp}stuff"
行做了它应该做的事情,即将单词 stuff
附加到变量 的内容>tmp
(从文件中读取的一行)。
从输入文件中读取的第一行包含字符串 http://www.example1.com
,后跟 CR
字符。追加字符串stuff
后,变量tmp
的内容为:
http://www.example1.com$'\r'stuff
CR
字符不可打印。它在终端上打印时有一个特殊的解释:它在行的开头(第 1 列)发送光标而不更改行。
当 echo
打印上面的行时,它会打印(从新行开始)http://www.example1.com
,然后是 CR
字符,将光标发送回打印字符串 stuff
的行首。 stuff
片段覆盖了该行 (http:
) 上已打印的前 5 个字符,结果在屏幕上显示为:
stuff//www.example1.com
解决方案是从输入文件中删除 CR
字符。有多种方法可以实现这一目标。
从输入文件中删除 CR
字符的简单方法是使用以下命令:
sed -i.bak s/$'\r'//g file.list
它从文件 file.list
的内容中删除所有 CR
字符,将更新后的字符串保存回 file.list
文件并将原始 file.list
文件存储为 file.list.bak
(一个备份副本,以防它没有产生您期望的输出)。
另一种删除 CR
字符的方法是要求 shell 在附加 stuff
的命令中删除它:
tmp="${tmp/$'\r'/}stuff"
当一个变量在像${tmp/a/b}
这样的构造中扩展时,a
在$tmp
中的所有出现替换为 b
。在这种情况下,我们将 \r
替换为空。
关于linux - bash 中的字符串连接导致替换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43963118/