为什么我在数字比较中得到命令未找到错误?

时间:2015-10-16 16:18:36

标签: linux bash shell comparison

我正在尝试解析文件的每一行并查找特定的字符串。该脚本似乎正在执行其预期的工作,但是,并行尝试在第6行执行if命令:

#!/bin/bash
for line in $(cat $1)
do
echo $line | grep -e "Oct/2015"
  if($?==0); then
  echo "current line is: $line"
  fi
done

我得到以下内容(我的脚本是readlines.sh)

./readlines.sh: line 6: 0==0: command not found

4 个答案:

答案 0 :(得分:2)

第一:正如Llama先生所说,你需要更多的空间。现在,您的脚本会尝试查找名为/usr/bin/0==0的文件来运行。代替:

[ "$?" -eq 0 ] # POSIX-compliant numeric comparison
[ "$?" = 0 ]   # POSIX-compliant string comparison
(( $? == 0 ))  # bash-extended numeric comparison

第二:在这种情况下,根本不要测试$?。事实上,你甚至没有充分理由使用grep;以下两者都更有效(因为它只使用内置于bash中的功能,不需要调用外部命令)并且更具可读性:

if [[ $line = *"Oct/2015"* ]]; then
  echo "Current line is: $line"
fi

如果您确实 需要使用grep,请按以下方式编写:

if echo "$line" | grep -q "Oct/2015"; then
  echo "Current line is: $line"
fi

这种方式if直接在管道的退出状态下运行,而不是运行第二个命令测试$?并在上运行命令&#39}退出状态。

答案 1 :(得分:2)

@pharles Duffy有一个很好的答案我已经投票正确(并且是),但是这里有详细的,逐行分解你的脚本以及正确的事情要做的每一部分。

for line in $(cat $1)

正如我在其他地方的评论中所指出的,这应该是while read构造而不是for cat构造。

  • 这个构造将使每个行分开,使文件中的空格分开"行"在输出中。
  • 将跳过所有空行。

此外,当您{cat} $1时,应引用该变量。如果没有引用空格,文件名中出现的其他不太常见的字符将导致cat失败,循环将不处理该文件。

完整的一行如下:

while IFS= read -r line

权衡can be found here的说明性示例。链接的测试脚本如下。我试图说明为什么IFS=-r很重要。

#!/bin/bash

mkdir -p /tmp/testcase
pushd /tmp/testcase >/dev/null

printf '%s\n' '' two 'three three' '' '    five with leading spaces' 'c:\some\dos\path' '' > testfile

printf '\nwc -l testfile:\n'

wc -l testfile

printf '\n\nfor line in $(cat) ... \n\n'

let n=1
for line in $(cat testfile) ; do
    echo line $n: "$line"
    let n++
done

printf '\n\nfor line in "$(cat)" ... \n\n'

let n=1
for line in "$(cat testfile)" ; do
    echo line $n: "$line"
    let n++
done

let n=1
printf '\n\nwhile read ... \n\n'

while read line ; do
    echo line $n: "$line"
    let n++
done < testfile

printf '\n\nwhile IFS= read ... \n\n'

let n=1
while IFS= read line ; do
    echo line $n: "$line"
    let n++
done < testfile

printf '\n\nwhile IFS= read -r ... \n\n'

let n=1
while IFS= read -r line ; do
    echo line $n: "$line"
    let n++
done < testfile

rm -- testfile

popd >/dev/null

rmdir /tmp/testcase

请注意,这是一个重要的例子。例如,其他shell不会支持-r进行读取,也不会let移植。转到脚本的下一行。

do

作为一种风格问题,我更喜欢与forwhile声明在同一行,但是没有约定。

echo $line | grep -e "Oct/2015"

此处应引用变量$line。一般来说,除非你明确地知道更好的,否则你应该双重引用所有扩展 - 这意味着子单元和变量。这使您免受大多数意外的贝壳怪异的影响。

你宣布你的shell为bash,这意味着你将拥有&#34;这里的字符串&#34;运营商<<<可供您使用。可用时,它可用于避免管道;管道的每个元素都在子shell中执行,这会产生额外的开销,如果您尝试修改变量,可能会导致意外行为。这将写成

grep -e "Oct/2015" <<<"$line"

请注意,我引用了line扩展。

您已使用grep致电-e,这不是不正确的,但由于您的模式不是以-开头,因此不必要。另外,你在shell中有一个完整引用的字符串,但你不会尝试扩展变量或在其中使用其他shell插值。如果您不希望并且不希望shell引用字符串的内容被视为特殊字符串,则应单引号。此外,使用grep是低效的:因为您的模式是固定字符串而不是正则表达式,您可以使用fgrepgrep -F,它使用字符串包含而不是正则表达式匹配(并因此而快得多)。所以这可能是

grep -F 'Oct/2015' <<<"$line"

不改变行为。

if($?==0); then

这是您原始问题的根源。在shell脚本中,命令由空格分隔;当你说if($?==0) $?扩展时,可能会为0,而bash将尝试执行名为if(0==0)的命令,这是一个合法的命令名称。你想要做的是调用if命令并给它一些参数,这需要更多的空格。我相信其他人已经充分涵盖了这一点。

您永远不需要在shell脚本中测试$?的值。 if命令存在基于您传递给它的任何命令的返回代码的分支行为,因此您可以内联grep调用并让if直接检查其返回代码,因此:

if grep -F 'Oct/2015` <<<"$line" ; then

请注意;分隔符周围的大量空格。我这样做是因为在shell中通常需要空格,并且只能省略某些内容。而不是试图记住你什么时候可以做,我建议在一切周围额外填充一个空间。它从来没有错,可以让其他错误更容易被注意到。

正如其他人已经注意到的那样grep会将匹配的行打印到stdout,这可能不是你想要的。如果您使用的是Linux上的标准GNU grep,那么您可以使用-q开关。这将抑制grep

的输出
if grep -q -F 'Oct/2015' <<<"$line" ; then

如果您要严格遵守标准,或者在grep并且不知道-q的任何环境中,实现此效果的标准方法是将stdout重定向到{{ 1}}

/dev/null/

在这个例子中,我还删除了这里的字符串bashism,以显示该行的可移植版本。

if printf "$line" | grep -F 'Oct/2015' >/dev/null ; then

这一行的脚本没有任何问题,只是虽然echo "current line is: $line" 是标准的实现,但是它的程度不同,以至于它不可能完全依赖它的行为。您可以在echo的任何地方使用printf,并且您可以对其打印内容充满信心。即使echo有一些警告:一些不常见的转义序列不是均匀支持的。有关详细信息,请参阅mascheck

printf

注意最后的显式换行符; printf不会自动添加一个。

printf 'current line is: %s\n' "$line"

对这一行没有评论。

    fi

如果您按照我的建议执行操作并将done 行替换为for构造,则此行将更改为:

while read

这将done < "$1" 变量中文件的内容定向到$1循环的标准输入,后者又将数据传递给while

为了清楚起见,我建议先将read中的值复制到另一个变量中。这样,当你阅读这一行时,目的就更明确了。

我希望没有人会对上面提到的风格选择采取重大攻击,这是我试图注意到的;有很多方法可以做到这一点(但不是很多正确的)方式。

当您在将来遇到这样的困难时,请务必通过优秀的shellcheckexplain shell运行有趣的摘要。

最后,这里列出了所有内容:

$1

答案 2 :(得分:1)

如果你喜欢单行,你可以使用AND运算符(echo "$line" | grep -e "Oct/2015" && echo "current line is: $line" ),例如:

grep -qe "Oct/2015" <<<"$line" && echo "current line is: $line"

或:

{{1}}

答案 3 :(得分:0)

间距在shell脚本中非常重要 此外,双parens用于数字比较,而不是单个parens。

if (( $? == 0 )); then