为什么我的bash shell脚本适用于v 3.2.5并且在4.1.2上失败?

时间:2015-03-21 04:28:53

标签: arrays linux bash shell

我的bash shell脚本正在使用Bash 3.2.5。 我有一个包含以下内容的输入文件:

1234567
2345678
3456789
4567890

和一个包含以下代码的bash shell脚本:

#!/bin/bash
content=($(cat data.txt | awk {'print $1'}))
for (( i=0 ; i<${#content[@]} ; i++ ))
do
  if (( i==0 ))
  then
    ele=`echo ${content[$i]}`
  else
    ele=`echo ","${content[$i]}`
  fi
  all=`echo $all$ele`
done
# should be a string of csv: works on bash 3.2.5 - fails on bash 4.1.2
echo $all

当使用Bash 3.2.5运行时,它输出:(预期输出;正确)

1234567,2345678,3456789,4567890

但是当使用Bash 4.1.2运行时,它输出:(文件中的最后一个数字;不正确)

,4567890

为什么Bash 4.1.2中的相同代码失败?

4 个答案:

答案 0 :(得分:2)

使用echo设置变量的内容只是简单..错误。当我说错了,我的意思是,你可以做到,但在这种情况下,绝对没有理由使用command substitution语句的echo来填充变量。同样,没有理由使用cat管道传输到awk来填充数组。只需简单的重定向即可轻松完成数组填充。

通过消除所有不需要的command substitution echopiped命令,您可以消除大量错误机会。这是一个适用于3或4的更新。

#!/bin/bash
content=( $( <data.txt) )
for (( i=0 ; i<${#content[@]} ; i++ ))
do
    if (( i==0 ))
    then
        ele="${content[i]}"
    else
        ele=",${content[i]}"
    fi
    all="${all}${ele}"
done
echo $all

<强>输出

$ bash bash34fail.sh
1234567,2345678,3456789,4567890

答案 1 :(得分:2)

这无助于理解您所看到的症状,但您的代码可以完全重写为

paste -d , -s data.txt

如果你想使用bash结构:

mapfile -t numbers < data.txt   # read the lines of the file into an array
(IFS=,; echo "${numbers[*]}")   # join the array with a comma

但是,请检查您的数据文件是否包含回车符:oc -d data.txt并使用dos2unixsed -i 's/\r$//' data.txt

进行修复

答案 2 :(得分:0)

将突出的评论转换为答案。

分配如:

ele=`echo ","${content[$i]}"`
all=`echo $all$ele`

不应该使用反向标记`…`$(…)命令替换;你应该写简单

all="$all$sep${content[$i]}"

您可以在循环之前获得sep="",并在分配给所有人之后获得sep=","

另外,FWIW,我在Mac OS X 10.10.2上尝试使用Bash版本3.2.57(1)-release和4.3.27(2)-release的脚本,并获得相同的正确(预期)输出都。

  • 您是否查看过bash -x yourscript.sh会发生什么?这是一个基本且非常重要的shell脚本调试技术。

  • 您是否检查了数据文件以获取Windows样式的CRLF行结尾?

当我的data.txt文件具有Windows样式的CRLF(回车,换行)行结尾时,我得到了&#39; Bash 4.1.2&#39;输出。当我的data.txt文件具有Unix风格的NL(换行,也就是换行)行结尾时,我得到了&#39; Bash 3.2.5输出。

您是否以某种方式通过Windows系统将材料复制到运行Bash 4.1.2的计算机上?或者,换句话说,您确定在要测试它的两台机器上运行完全相同的数据文件吗?

我将脚本简化为:

sep=""
all=""
while read number
do
    all="$all$sep$number"
    sep=","
done < data.txt
echo "$all"

还有其他方法可以达到相同的效果,但这种方法非常简单,并且不会使用任何子壳,这与使用大量脚本的原始脚本不同。

答案 3 :(得分:0)

这个问题的答案是;由于文件中的crlf在4.1.2上运行的脚本正在使用。

Jonathan Leffler和Glenn jackman让我回答了这个问题。 我在运行bash 4.1.2的linux机器上创建的文件是用crlf创建的dos风格。使用lf作为行结束标记创建3.2.5文件。我没想到要查看数据文件本身,我很清楚这会导致过去与其他情况一起出现问题。

我从上面的所有帖子中了解了不同的更好的方法,我会全力以赴。

再次感谢你。

以下是我正在研究的两个系统的更多细节:

bash 4.2.1:

$ uname -a
Linux n0c 2.6.32-358.18.1.el6.x86_64 #1 SMP Fri Aug 2 17:04:38 EDT 2013 x86_64     x86_64 x86_64 GNU/Linux
$ bash -version
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

bash 3.2.5:

[root@ww ~]# uname -a
Linux ww.com 2.6.18-028stab101.1-ent #1 SMP Sun Jun 24 20:22:25 MSD 2012 i686   i686 i386 GNU/Linux
[root@ww ~]# bash -version
GNU bash, version 3.2.25(1)-release (i386-redhat-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.