bash脚本的奇怪行为

时间:2011-05-20 14:22:43

标签: bash

这是一个片段:

var=`ls | shuf | head -2 | xargs cat | sed -e 's/\(.\)/\1\n/g' | shuf | tr -d '\n'`

这将从当前目录中选择两个随机文件,组合它们的内容,将它们混洗,并将结果分配给var。这在大多数情况下都可以正常工作,但是在大约一千个案例中,只有ls的输出被绑定到var(它不仅仅是输出,请参阅编辑II)。可能是什么解释?

一些可能相关的事实:

  • 目录包含至少两个文件
  • 目录中只有文本文件
  • 文件名不包含空格
  • 文件长度为5到约1000个字符
  • 该代码段是一个较大的脚本的一部分,它并行运行了两个实例
  • bash版本:GNU bash, version 4.1.5(1)-release (i686-pc-linux-gnu)
  • uname:Linux 2.6.35-28-generic-pae #50-Ubuntu

编辑:我自己运行了几千次代码,没有任何错误。然后我尝试用整个脚本的其他各个部分运行它。这是一个产生错误的配置:

cd dir_with_text_files
var=`ls | shuf | head -2 | xargs cat | sed -e 's/\(.\)/\1\n/g' | shuf | tr -d '\n'`
cd ..

cd之间有几百行脚本,但这是重现错误的最小配置。请注意,异常输出绑定到var当前目录的输出,而不是dir_with_text_files

编辑II:我一直在更详细地查看输出。 ls输出不会单独出现,它与两个混洗文件(在其内容之间,或之后或之前,完整)一起出现。但它变得更好;让我开始讨论特定的目录。

[~/projects/upload] ls -1
checked // dir
lines   // dir, the files to shuffle are here
pages   // also dir
proxycheck
singlepost
uploader
indexrefresh
t
tester

到目前为止,我已经看到ls的输出来自upload,但现在我看到ls */*的输出(也来自upload)。它的形式为“someMangledText ls moreMangledText ls */* finalBatchOfText”。毫无疑问,生成的序列ls是否可能以某种方式执行?

3 个答案:

答案 0 :(得分:2)

这里也没有问题。 我还要重写上面的内容:

sed 's:\(.\):\1\n:g' < <(shuf -e * | head -2 | xargs cat) | shuf | tr -d '\n'

请勿使用ls列出目录的内容,请使用* 而且,做一些调试。使用shebang,然后:

set -e
set -o pipefail

并像这样运行脚本:

/bin/bash -x /path/to/script

并执行检查输出 您可以使用-x

围绕看似有问题的部分,而不是调试整个脚本
set -x
...code that may have problems...
set +x

以便输出集中在代码的那一部分。 另外,请使用pipefail选项。

  

一些定义:

     
      
  • -e:如果一个简单的命令以非零状态退出,则立即退出,除非失败的命令是紧跟在while或until关键字之后的命令列表的一部分,if语句中的部分测试,&amp;&amp;和或||列表,或者如果使用!反转命令的返回状态。 ERR上的陷阱(如果已设置)在shell退出之前执行
  •   
  • -x:打印一系列简单命令,用于命令,大小写命令,选择命令,以及命令及其参数或相关单词列表在扩展后和执行之前的算术运算。扩展PS4变量的值,并在命令及其扩展参数
  • 之前打印结果值   
  • pipefail:如果设置,管道的返回值是以非零状态退出的最后一个(最右边)命令的值,如果管道中的所有命令都成功退出则为零
  •   

答案 1 :(得分:1)

出于调试目的,您还可以使用env -i清除环境并过滤掉不可打印的字符:

#!/usr/bin/env -i /bin/bash --

set -ef
set -o pipefail

unset IFS PATH LC_ALL
IFS=$' \t\n'
PATH="$(PATH=/bin:/usr/bin getconf PATH)"
LC_ALL=C
export IFS PATH LC_ALL

#var="$((find . -type f -maxdepth 1 -print0 | shuf -z -n 2 | xargs -0 cat) | sed -e 's/\(.\)/\1\n/g' | shuf | tr -d '\n')"

var="$((find . -type f -maxdepth 1 -print0 | shuf -z -n 2 | xargs -0 cat) | tr -cd '[[:print:]]' | grep -o '.' | shuf | tr -d '\n')"

在运行脚本之前,您还可以禁用GNU readline库和!风格历史扩展:

bash --noediting
set +H

答案 2 :(得分:0)

根据你对你的失败率所说的话,并且考虑到上述海报所执行的其他测试的成功,这听起来像偶尔的目录更改失败可能导致的问题。您在此脚本中访问的目录是否偶然从远程计算机挂载?如果是这样,它可能只是一个小的和临时的网络相关的故障,没有得到妥善处理。 (只是一个猜测。)