【问题标题】:concatenation of strings in bash results in substitutionbash 中字符串的串联导致替换
【发布时间】:2017-05-14 11:01:27
【问题描述】:

我需要将一个文件读入一个数组并在每行的末尾连接一个字符串。这是我的 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 OS。

通过awkprintfecho 命令连接字符串时也会出现此问题。例如echo $tmp"stuff"echo "${tmp}""stuff"

【问题讨论】:

  • 我检查了你的代码似乎没问题,并发现如预期的那样http://www.example1.comstuff http://www.example2.comstuff
  • 可能是因为我的电脑是Mac?
  • 无法重现,你的代码给我http://www.example1.comstuff http://www.example2.comstuff
  • 我已经在 Mac 和 Vagrant box (Ubuntu 16.04) 中测试过
  • @Yves 一个标题不佳的问题,带有误导性的公认答案,对社区几乎没有帮助。这有可能是有价值的。也许您至少应该将问题重命名为人们可能会搜索的内容,并选择一个比不直接解决核心问题但更多可能解决方法的答案更可靠。

标签: linux windows bash macos string-concatenation


【解决方案1】:

./file.lst 文件很可能是在 Windows 系统上生成的,或者至少是使用 Windows 行尾约定保存的。

Windows 使用两个字符的序列来标记文本文件中的行尾。这些字符是CR (\r) 后跟LF (\n)。类 Unix 系统(Linux 和 macOS 从版本 10 开始)使用 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 替换为空。

【讨论】:

  • @hmedia1 我看不出你的回答如何解释你说我不明白的所有事情。我也看不到您编辑的 Ohad 的答案是如何回答这个问题的。如果 OP 手头没有 Windows 盒子怎么办?
  • @hmedia1,如果您指出 axiac 答案的不准确之处,而不是仅仅声称有一些,您的评论会更有用。对于一个人来说,我很想听到更多关于它有什么问题的信息,因为我真的看不出任何问题。另外,如果您认为原始问题中的问题不是回车,也许您应该发布自己的答案?
  • @axiac 感谢您的回答! ${tmp/a/b} 工作得很好。 +1
【解决方案2】:

我猜这与Carriage Return 字符有关。 你的file.list 是在 Windows 上创建的吗?如果是这样,请在运行脚本之前尝试使用dos2unix

编辑

您可以使用file 命令检查您的文件。

例子:

file file.list

如果您像这样将文件保存在 Windows 记事本中:

那么它可能会出现这样的情况:

  • file.list:ASCII 文本,没有行终止符

您可以使用iconv 等内置工具来转换编码。但是,对于这样的简单用途,您可以使用适用于多种编码的命令,而无需进行任何转换。

您可以简单地通过cat 缓冲文件,并使用适用于以下任一情况的正则表达式:

  • 回车后跟行终止符,或
  • 单独的行终止符

然后追加字符串。

示例:

cat file.list | grep -E -v "^$" | sed -E -e "s/(\r?$)/stuff/g" 

适用于 ASCII 文本和不带行终止符的 ASCII 文本。

【讨论】:

  • 有比使用第三方工具更好的方法来检查和转换字符编码。这是一个非常不完整的答案。
  • @hmedia1 肯定有。这只是表明这是问题的一种简单方法(OP 没有意识到)
  • 如果这是一个问题,file file.list 会突出显示,例如:file.list: ASCII text, with no line terminators - 同样,开箱即用,可以集成到解决方案中。
【解决方案3】:

如果您需要修改流以附加固定字符串,可以使用sedawk,例如:

sed 's/$/stuff/'

将内容附加到每行的末尾。

【讨论】:

    猜你喜欢
    • 2015-02-05
    • 1970-01-01
    • 2012-08-09
    • 2011-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多