bash脚本中的错误:算术错误

bash脚本中的错误:算术错误,bash,Bash,我编写了一个简单的脚本,从一堆文件(*.out)中提取文本,并在开头添加两行,在结尾添加一行。然后,我将提取的文本与另一个文件一起添加,以创建一个新文件。脚本在这里 #!/usr/bin/env bash #A simple bash script to extract text from *.out and create another file for f in *.out; do #In the following line, n is a number which is extract

我编写了一个简单的脚本,从一堆文件(
*.out
)中提取文本,并在开头添加两行,在结尾添加一行。然后,我将提取的文本与另一个文件一起添加,以创建一个新文件。脚本在这里

#!/usr/bin/env bash
#A simple bash script to extract text from *.out and create another file 
for f in *.out; do
#In the following line, n is a number which is extracted from the file name
        n=$(echo $f | cut -d_ -f6)
        t=$((2 * $n ))
#To extract the necessary text/data
        grep "  B  " $f | tail -${t} | awk 'BEGIN {OFS=" ";} {print $1, $4, $5, $6}' | rev | column -t | rev > xyz.xyz
#To add some text as the first, second and last lines.
        sed -i '1i -1 2' xyz.xyz
        sed -i '1i $molecule' xyz.xyz
        echo '$end' >> xyz.xyz
#To combine the extracted info with another file (ea_input.in)
        cat xyz.xyz ./input_ea.in > "${f/abc.out/pqr.in}"
     done

./script.sh: line 4: (ls file*.out | cut -d_ -f6: syntax error: invalid arithmetic operator (error token is ".out) | cut -d_ -f6")
如何更正此错误

在bash中,当您使用:

$(( ... ))
$( ... )
它将括号中的内容视为算术表达式,返回计算结果,并且在使用时:

$(( ... ))
$( ... )
它执行括号中的内容并返回输出

因此,要解决您的问题,应该简单到将第4行替换为:

n=$(ls $f | cut -d_ -f6)

这将用单括号替换外部双括号,并删除不必要的
ls$f
周围的附加括号。

可以通过在括号之间添加空格来避免算术错误。您已经在脚本的其他地方正确地使用了
var=$((算术表达式))
,因此应该很容易理解
$((ls“$f”)| cut-d_6))
为什么需要一个空格。但是次壳也是完全多余的;您需要
$(ls“$f”| cut-d\u6)
。除了
ls
在这里也没有做任何有用的事情;使用
$(echo“$f”| cut-d\u6)
。除此之外,shell可以很容易地(尽管有点笨拙)通过参数替换提取子字符串<代码>“${f}*.*.*.*.*.*.*.*.}”。除非您在脚本中使用了Awk,否则在Awk中执行此操作(以及更多操作)更有意义

下面是一个将大部分处理重构为Awk的例子

for f in *.out; do
     awk 'BEGIN {OFS=" " }
            # Extract 6th _-separated field from input filename
            FNR==1 { split(FILENAME, f, "_"); t=2*f[6] }
            # If input matches regex, add to array b
            /  B  / { b[++i] = $1 OFS $4 OFS $5 OFS $6 }
            # If array size reaches t, start overwriting old values
            i==t { i=0; m=t }
            END {
                # Print two prefix lines
                print "$molecule"; print -1, 2;
                # Handle array smaller than t
                if (!m) m=i
                # Print starting from oldest values (index i + 1) 
                for(j=1; j<=m; j++) {
                    # Wrap to beginning of array at end
                    if(i+j > t) i-=t
                    print b[i+j]; }
                print "$end" }' "$f" |
        rev | column -t | rev |
        cat -  ./input_ea.in > "${f/foo.out/bar.in}"
     done
用于f in*.out;做
awk'开始{OFS=”“}
#从输入文件名中提取第6个分隔字段
FNR==1{split(文件名,f,“”);t=2*f[6]}
#如果输入匹配正则表达式,则添加到数组b
/B/{B[++i]=$1 OFS$4 OFS$5 OFS$6}
#如果数组大小达到t,则开始覆盖旧值
i==t{i=0;m=t}
结束{
#打印两行前缀
打印“$molecular”;打印-1,2;
#句柄数组小于t
如果(!m)m=i
#从最早的值开始打印(索引i+1)
对于(j=1;j t)i-=t
打印b[i+j];}
打印“$end”}“$f”|
版次|列-t |版次|
cat-./输入\输入>“${f/foo.out/bar.in}”
完成
还要注意我们如何避免使用临时文件(如果没有Awk重构,这当然也是可以避免的),以及我们如何注意用双引号引用所有文件名变量


数组
b
包含(最多)来自匹配行的最新
t
值;当我们到达索引
t
时,通过将索引
i
包装回数组的开头,我们将这些值收集到一个数组中,该数组被限制为永远不包含超过
t
的值。这种“循环数组”避免了在内存中保留太多的值,如果输入文件包含许多匹配项,这会使脚本速度变慢。

直接的问题是括号太多;但实际上,如果您使用的是Awk,请将所有这些重构为Awk。在您刚刚用Awk创建的文件上运行
sed-i
(两次!)是很容易避免的,坦率地说是非常可怕的。感谢您的更新。能否请您也包括样本输入的预期输出?谢谢。我按照你的建议换了第四行。现在,出现了一个新的错误/script.sh:第5行:2*08:value对base太大(错误标记为“08”),算术上下文中数字的前导零会导致shell将其视为八进制。修剪前导零本身很简单。如何修剪脚本中的前导零?我添加了n=$(ls$f | cut-d |-f6 | sed's/^0*/')。现在又出现了另一个错误:./script.sh:第5行:2*:语法错误:预期的操作数(错误标记为“*”)。我猜有些
*。out
文件的第6个字段中没有合适的数字,请在第5行之前尝试
echo${n}
对其进行调试。我发现很难理解awk内部的情况。现在,我正在运行脚本(使用set-x)。我正在处理100个文件。现在需要很长时间。对于所有文件,-1 2被附加到文件中的输入_ea。使用/B/etc提取的文本不会追加。另外,变量$molecular和$end没有附加到input_ea.in.中。我不得不猜测一些事情,显然没有办法测试这一点。
molecular
end
是Bash变量的名称,还是应该逐字包含的静态字符串?
set-x
只影响Bash的详细程度,而不影响Awk。您可以通过在脚本的各个点添加
print
语句来检查脚本中发生了什么。仍然不清楚您是否有一对未记录的变量,或者您是否希望文本
$molecular
$end
围绕摘录的值。我已经更改了脚本以执行后者。