1

I am trying to create a bash script to perform the same action of the below command :

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

Output is google-public-dns-a.google.com.

Now I have created the script test.sh as below :

#!/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

and called the script as ./test.sh 8.8.8.8. But getting errors. How can I store Linux commands inside bash variables effectively with quotes ?
Thanks

18
  • Ditch bash. Use python Commented Sep 27, 2013 at 22:08
  • @ealfonso Ditch python. Write assembly code. Do I win the "cool programmer" contest? Commented Sep 27, 2013 at 22:22
  • Python is in quite the opposite direction from assembly code Commented Sep 27, 2013 at 22:25
  • 1
    @ealfonso Meh; cut might break easier than a regex, it might not - you might have well-defined fields but no idea what was in them. And both shell scripting and Python would let you use both anyway, in multiple different flavours. Like I say, it's all kind of subjective, and that was kind of where this discussion started. I think we both started taking it too seriously somewhere along the line, for which I apologise. Commented Sep 28, 2013 at 0:04
  • 1
    Yeah, I'm also guilty of taking it a bit too personally. I'm glad that you challenged me, I think a better and more concrete/helpful discussion came out of this than my original comment could ever have implied. Commented Sep 28, 2013 at 1:39

4 Answers 4

4

There are a couple of problems here.

The first is that you're trying to execute as a command the contents of the variable $ouptut1, which is just the dig output; it's not a command. You probably meant echo $output1.

Then you are trying to execute the contents of variable $grepg as a pipeline. But parsing a line into the components of a compound command (a pipeline is a compound command) happens before parameter expansion. So if you execute something like this:

foo="echo hello | grep hi"
$foo

This first gets broken apart into a single simple command, then the command is expanded, then it is parsed into arguments. So this is equivalent to running:

echo hello "|" grep hi

In general, it's not a good idea to store strings of text that you want to have evaluated in variables. There are a few very special cases in which you may want to do it, but in almost every case (and probably your case) there's a better way to do it. I'm going to warn you against it, but if you really want to evaluate a string as a command, you can use eval, for example:

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

What you seem to want is to define a shell function. If you want to reuse that pipeline several times, running several different values through it, just define a shell function. That function is a single command that acts as the equivalent of the contained commands:

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

go=$(echo $output1 | grepg)

To simplify your code further, you can avoid all of the intermediate variables. If you're just going to pipe the results of the dig command into grepg, you don't need to save it in a variable before doing so. And if you're just going to echo the results to stdout, again you don't need to store it in a variable, you can just let the output go to stdout:

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

dig -x $IP | grepg

I'm assuming here that you wanted to factor out the function so you could use it several times in your script; if not, if you're just going to use it once, you could simplify further to:

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

Note that if you want to store a pipeline like this in a variable so that you could use some other code to decide between two functions, and apply one or the other depending on some condition, I'd recommend defining the function like above, and then just storing the single function name in the variable. That will work the way you expect:

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
Sign up to request clarification or add additional context in comments.

Comments

3

Why not simply:

dig -x "$1" | grep PTR | cut -d ' ' -f 2 | grep google | cut -f 5

1 Comment

I have used this to convey my doubt clearly. I know it can be done in single step but want to understand how to store the commands inside variables and use for later use.
1

output1 stores the results of dig -x $IP, not the command itself since you're using $( ).

grepg stores the command as a string, which is fine but is giving you some unexpected results.

On this line:

go=$($output1 | $grepg) 

it tries to pipe the output from dig into a single program called "grep PTR | cut -d ' ' -f 2 | grep google | cut -f 5".

What you probably want is:

#!/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

The dig command is stored as a string in output1, just like grepg stores the other command.

Also, eval is needed here as well. Without eval it thinks that the entire string is the program name, instead of a program name followed by arguments, pipes, etc.

That means it's looking for a program called "grep PTR | cut -d ' ' -f 2 | grep google | cut -f 5" rather than looking for grep followed by an argument, followed by a pipe, and so on.

1 Comment

Yes this is what I really looked into
0

Your piped chain of commands can be shortened to a single awk command:

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

I case you want to search for google also in the same line:

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

To store its output in a bash variable, use:

go=$(dig -x 8.8.8.8| awk '/PTR[[:space:]]/{print $NF}')

2 Comments

@IMSoP: That grep isn't really needed since this IP resolves to google-public-dns-a.google.com
It's pretty clear in the question that 8.8.8.8 is variable, but google is constant. It's not explained why this is necessary, but nor is there evidence that it's not.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.