为什么在将此变量与字符串组合时会替换此变量的一部分?

时间:2018-05-26 05:10:10

标签: linux bash

我有以下Bash脚本循环遍历文件行:

INFO_FILE=playlist-info-test.txt
line_count=$(wc -l $INFO_FILE | awk '{print $1}')

for ((i=1; i<=$line_count; i++))
do
    current_line=$(sed "${i}q;d" $INFO_FILE)

    CURRENT_PLAYLIST_ORIG="$current_line"
    input_file="$CURRENT_PLAYLIST_ORIG.mp3"
    echo $input_file
done

这是playlist-info-test.txt文件的示例:

Playlist 1
Playlist2
Playlist 3

脚本的输出应如下所示:

Playlist 1.mp3
Playlist2.mp3
Playlist 3.mp3

但是,我得到以下输出:

.mp3list 1
.mp3list2
.mp3list 3

我已经花了几个小时在这上面,并且无法理解为什么&#34; .mp3&#34;部分被移动到字符串的前面。我最初认为这是因为输入文件的行中有空格,但删除空格并没有什么不同。我还尝试使用带有read line的while循环,并将输入文件重定向到它,但这也没有任何区别。

2 个答案:

答案 0 :(得分:2)

我复制了playlist-info-test.txt内容和脚本,并获得了您期望的输出。很可能在playlist-info-test.txt或脚本中存在不可打印的字符,这些字符会使处理变得混乱。使用例如xxd -g 1检查两个文件的二进制内容,并查找非换行(0a)非打印字符。

答案 1 :(得分:0)

该文件是否来自Windows? DOS和Windows以回车符结束(十六进制0d,有时表示为\r),后跟换行符(十六进制0a,有时表示为\n)。 Unix只使用换行,因此倾向于将回车视为行内容的一部分。在你的情况下,它会在current_line变量的末尾结束,因此input_file会出现类似&#34;播放列表1 \ r.mp3&#34;的内容。当您将其打印到终端时,回车使其返回到行的开头(即回车的含义),因此打印为:

Playlist 1
.mp3

...使用&#34; .mp3&#34;打印在&#34; Play&#34;部分,而不是像我上面那样在下一行。

解决方案:要么修复文件(那里有一个相当标准的dos2unix程序,这样做),或者更改脚本以在读取文件时去除回车。实际上,无论如何我都建议重写,因为你目前使用sed来挑选线条是相当奇怪和低效的。在shell脚本中,逐行读取文件的标准方法是使用类似while read -r current_line; do [commands here]; done <"$INFO_FILE"的循环。可能的问题是,如果循环内的任何命令从标准输入读取,他们将最终吸入该文件的一部分;您可以通过将文件传递到单元3而不是标准输入来解决此问题。有了这个修复和修剪回车的技巧,这就是它的样子:

INFO_FILE=playlist-info-test.txt

while IFS=$' \t\n\r' read -r current_line <&3; do
    CURRENT_PLAYLIST_ORIG="$current_line"
    input_file="$CURRENT_PLAYLIST_ORIG.mp3"
    echo "$input_file"
done 3<"$INFO_FILE"

(回车修剪由read完成 - 它总是自动修剪前导和尾随空格,并将IFS设置为$' \t\n\r'告诉它处理空格,制表符,换行符并且回车符为空格。由于该赋值是read命令的前缀,因此它仅适用于那一个命令,之后您不必将IFS恢复正常。 )

我在这里的其他一些建议:双引所有变量引用(就像我上面的echo "$input_file"所做的那样),并避免使用全大写变量名(有一堆具有特殊含义,如果你不小心使用其中一个,它可能有奇怪的效果)。哦,尝试将脚本传递给shellcheck.net - 它擅长发现常见错误。