1

我正在尝试创建一个 bash 脚本来执行以下命令的相同操作:

dig -x 8.8.8.8 | grep PTR | cut -d ' ' -f 2 | grep google | cut -f 5

输出是google-public-dns-a.google.com.

现在我创建了脚本 test.sh 如下:

#!/bin/bash
IP=$1
output1=$(dig -x $IP)
grepg="grep PTR | cut -d ' ' -f 2 | grep google | cut -f 5"
go=$($output1 | $grepg)
echo $go

并将脚本称为 ./test.sh 8.8.8.8。但是出现错误。如何使用引号有效地将 Linux 命令存储在 bash 变量中?
谢谢

4

4 回答 4

4

这里有几个问题。

第一个是您试图将变量的内容作为命令执行$ouptut1,这只是dig输出;这不是命令。你可能的意思是echo $output1

然后,您尝试将变量的内容$grepg作为管道执行。但是将一行解析为复合命令的组件(管道是复合命令)发生在参数扩展之前。所以如果你执行这样的事情:

foo="echo hello | grep hi"
$foo

首先将其分解为一个简单的命令,然后扩展命令,然后将其解析为参数。所以这相当于运行:

echo hello "|" grep hi

通常,将要评估的文本字符串存储在变量中并不是一个好主意。在一些非常特殊的情况下,您可能想要这样做,但几乎在每种情况下(可能还有您的情况)都有更好的方法来做到这一点。我要警告你不要这样做,但如果你真的想将字符串评估为命令,你可以使用eval,例如:

go=$(echo $output1 | eval $grepg)

您似乎想要的是定义一个 shell 函数。如果您想多次重用该管道,通过它运行几个不同的值,只需定义一个 shell 函数。该函数是一个单独的命令,相当于包含的命令:

function grepg() {
    grep PTR | cut -d ' ' -f 2 | grep google | cut -f 5
}

go=$(echo $output1 | grepg)

为了进一步简化您的代码,您可以避免使用所有中间变量。如果您只是要将dig命令的结果通过管道传输到grepg中,则无需在执行此操作之前将其保存在变量中。如果您只是要将结果回显到标准输出,那么您也不需要将其存储在变量中,您可以让输出转到标准输出:

#!/bin/bash
IP=$1
function grepg() {
    grep PTR | cut -d ' ' -f 2 | grep google | cut -f 5
}

dig -x $IP | grepg

我在这里假设您想分解该函数,以便可以在脚本中多次使用它;如果没有,如果您只想使用一次,您可以进一步简化为:

#!/bin/bash
IP=$1
dig -x $IP | grep PTR | cut -d ' ' -f 2 | grep google | cut -f 5

请注意,如果您想将这样的管道存储在一个变量中,以便您可以使用其他代码在两个函数之间做出决定,并根据某些条件应用一个或另一个,我建议您定义上述函数,并且然后只需将单个函数名称存储在变量中。这将按您期望的方式工作:

function grepg() {
    grep PTR | cut -d ' ' -f 2 | grep google | cut -f 5
}

function grepf() {
    # do something else, I'm not creative enough to come up with a good example
}

if [ "$2" = "foo" ]
then
    func=grepg
else
    func=grepf
fi

dig -x $IP | $func
于 2013-09-27T22:04:54.880 回答
3

为什么不简单:

dig -x "$1" | grep PTR | cut -d ' ' -f 2 | grep google | cut -f 5
于 2013-09-27T21:41:52.470 回答
1

output1存储 的结果dig -x $IP,而不是命令本身,因为您使用的是$( ).

grepg将命令存储为字符串,这很好,但会给您一些意想不到的结果。

在这条线上:

go=$($output1 | $grepg) 

它试图将输出从管道传输dig到一个名为"grep PTR | cut -d ' ' -f 2 | grep google | cut -f 5".

你可能想要的是:

#!/bin/bash
IP=$1
output1="dig -x $IP"
grepg="grep PTR | cut -d ' ' -f 2 | grep google | cut -f 5"
go=$(eval $output1 | eval $grepg)
echo $go

dig命令以字符串形式存储在 中output1,就像grepg存储其他命令一样。

此外,eval这里也需要。如果没有eval它,则认为整个字符串是程序名称,而不是程序名称后跟参数、管道等。

这意味着它正在寻找一个被调用的程序,"grep PTR | cut -d ' ' -f 2 | grep google | cut -f 5"而不是寻找grep后跟一个参数,然后是一个管道,等等。

于 2013-09-27T21:46:22.943 回答
0

您的管道命令链可以缩短为单个 awk 命令:

dig -x 8.8.8.8 | awk '/PTR[[:space:]]/{print $NF}'
google-public-dns-a.google.com

我想您也想google在同一行中搜索:

dig -x 8.8.8.8| awk '/PTR[[:space:]]/ && /google/ {print $NF}'

要将其输出存储在 bash 变量中,请使用:

go=$(dig -x 8.8.8.8| awk '/PTR[[:space:]]/{print $NF}')
于 2013-09-27T21:44:38.917 回答