Shell backticks within backticks

Backticks (`) perform a great function but I can't find a good reference for them, so thought I'd ask here in case anyone knew the answer...

Can you use backticks within backticks? If so I guess you would need to escape the inner backticks...
 
Yes, I believe you can. For example I use the following to find the prefix of the currently running script. With enough escaping, it could probably be made into a single line but it gets confusing ;)

Code:
DIRNAME=`dirname \`which $0\``
PREFIX=`cd $DIRNAME && cd .. && pwd`

There is also the $() syntax that might make things a bit easier (does the same thing):

Code:
DIRNAME=$(dirname `which $0`)
PREFIX=$(cd $DIRNAME && cd .. && pwd)
 
I'm trying to get this working:-
Code:
grep `whois \`fetch -qo - https://www.myexternalip.com/raw\` | grep country | awk '{ print $2 }'` zone.tabs
but get
Unmatched '`'.
Not sure if this is a syntax error or if I'm trying to do something which can't be done...
 
Depending on your shell you might also be able to use $() for command execution.

And sometimes escaping (using the \ character) can also help.

(edit) For what's it worth that command seems to run without any errors on my end. Using ksh though.
 
No time to debug it for you, but these really complex things are hard to debug.
Suggestion: Break it into little bits, and put the bits into variables:
Code:
a=`do foo`
b=`other $a`
c=`$b elefant`
and so on. If you do this, you can develop it one step at a time, and debug it by checking the content of the variables after each baby step.
 
ralphbsz makes a very good point. Since I feel generous, what the heck.. :cool:

Code:
grep `whois \`fetch -qo - https://www.myexternalip.com/raw\` | grep country | awk '{ print $2 }'` zone.tabs
So first.. there are some oddities here. fetch -qo - https://www.myexternalip.com/raw gives me a single IP address. So what are you trying to grep from that? See: grep(1):

Code:
     grep [-abcdDEFGHhIiJLlmnOopqRSsUVvwxZz] [-A num] [-B num] [-C[num]]
          [-e pattern] [-f file] [--binary-files=value] [--color[=when]]
          [--colour[=when]] [--context[=num]] [--label] [--line-buffered]
          [--null] [pattern] [file ...]
Note: [pattern] [file]. So search request first, input later.

So first step: whois `fetch -qo - https://www.myexternalip.com/raw` | grep country | awk '{ print $2 }', this gives me:

Code:
peter@zefiris:/home/peter $ w` | grep country | awk '{ print $2 }'            <
NL
So I think all you had to do was remove the first grep :)
 
No time to debug it for you, but these really complex things are hard to debug.
Suggestion: Break it into little bits, and put the bits into variables:
Code:
a=`do foo`
b=`other $a`
c=`$b elefant`
and so on. If you do this, you can develop it one step at a time, and debug it by checking the content of the variables after each baby step.


I did the baby steps - see Thread 67116... Just as an exercise could those baby steps be put together in one big step with backticks within backticks within backticks?
 
Yes you can. But I think you have to quote them; that may depend on the shell. I just tried it 2-levels deep in bash, and it worked fine:
Code:
echo foo `echo bar \`echo blatz\``
It is still insane. If someone were to try 3 levels deep, they deserve to be hit with a stick.
 
Try to prevent using nested backticks unless you want to deliberately obfuscate the code. Remember: you have to understand what you did there a few months or even years, possibly while troubleshooting with very limited time because someone is screaming at you. Keep even one-liners readable and easy to follow (and/or comment them!).

I'm usually using the $() notation at the very end/beginning of longer pipes, as (IMHO) they kind of resemble the punctiation for code blocks known from other (script) languages. Backticks - especially with some fonts and on smaller displays - are not that obvious and make the block hard to distinguish. You can also jump between the ( ) brackets with a single keystroke in most editors (vim: %) and when spanning over multiple lines (using '\'s) most editors also allow proper folding. All this isn't true for backticks (at least not out-of-the-box), so I'm using backticks only for short, single commands and not for longer constructs.


Regarding your code: to simplify the whole pipe I'd suggest to completely remove the "inner grep", especially because you already use awk:

Code:
country=$(whois `fetch -qo - https://www.myexternalip.com/raw` | awk '/country/ { print $2 }') && grep $country zone.tabs

or if it doesn't "need" to be a one-liner (e.g. for crontab), just make it 2 lines and omit the '&&':

Code:
#!/bin/sh

country=$(whois `fetch -qo - https://www.myexternalip.com/raw` | awk '/country/ { print $2 }')
grep $country zone.tab
 
Yes you can. But I think you have to quote them; that may depend on the shell. I just tried it 2-levels deep in bash, and it worked fine:
Code:
echo foo `echo bar \`echo blatz\``
It is still insane. If someone were to try 3 levels deep, they deserve to be hit with a stick.

If you can put the following into one line with backticks three levels deep you have my permission to hit me with a stick :)

Code:
IP=`fetch -qo - https://www.myexternalip.com/raw`
COUNTRY=`whois $IP | grep country | awk '{print $2}'`
ZONE=`grep $COUNTRY zone.tab | awk '{print $3}'`
echo ln -s /usr/share/zoneinfo/$ZONE /etc/localhost
 
If you can put the following into one line with backticks three levels deep you have my permission to hit me with a stick :)

Code:
IP=`fetch -qo - https://www.myexternalip.com/raw`
COUNTRY=`whois $IP | grep country | awk '{print $2}'`
ZONE=`grep $COUNTRY zone.tab | awk '{print $3}'`
echo ln -s /usr/share/zoneinfo/$ZONE /etc/localhost

Code:
echo ln -s /usr/share/zoneinfo/`grep \`whois \\\`fetch -qo - https://www.myexternalip.com/raw \\\` | awk '/country/ { print $2 }' \` /usr/share/zoneinfo/zone.tab | head -n1 | awk '{ print $3 }'` /etc/localhost

Had to add "head -n1" because e.g. "DE" is listed twice in the zone.tab.


Now just let me get that bamboo stick...



edit:
You can nest as deep as you like just by increasing the quoting by 2 quotes for every quote in the previous nesting level plus the single quote for actually quoting the backick; so number of quotes increases 1 -> 3 -> 7 -> 15 -> 31 ....
Code:
echo $((`echo $((\`echo $((\\\`echo $((\\\\\\\`echo $((\\\\\\\\\\\\\\\`echo $((\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\`echo 1\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\`+1))\\\\\\\\\\\\\\\`+1))\\\\\\\`+1))\\\`+1))\`+1))`+1))

Now I really have to wash my hands o_O
 
Code:
echo ln -s /usr/share/zoneinfo/`grep \`whois \\\`fetch -qo - https://www.myexternalip.com/raw \\\` | awk '/country/ { print $2 }' \` /usr/share/zoneinfo/zone.tab | head -n1 | awk '{ print $3 }'` /etc/localhost

Had to add "head -n1" because e.g. "DE" is listed twice in the zone.tab.

Now just let me get that bamboo stick...

That's excellent, although my eyes go funny when reading it.

Did you say it only works under bash?
 
I never use bash, this is not linux world... it was tested with standard bourne shell, so it "might" also work in bash.

edit:
just to make it clear: just because "it works", the abomination that is quoted backquotes should _NEVER_ be used for anything else than entertainment (if you'd like to call it that...).
Everyone finding something like that in production code shall cease all other activities until the author has been found and punished.
 
In [T]csh, double quotes are used for this purpose:
Code:
echo "`echo start "\`""\`""\`"echo "\""hello "\"""\`""\`"echo "\""world "\"""\`""\`"echo end"\`""\`""\`""\`""\`"`"
 
In [T]csh, double quotes are used for this purpose:
Code:
echo "`echo start "\`""\`""\`"echo "\""hello "\"""\`""\`"echo "\""world "\"""\`""\`"echo end"\`""\`""\`""\`""\`"`"

This does exactly the same....

Code:
echo `echo start` `echo hello` `echo world` `echo end`
 
May or may not be relevant but I notice that in /bin/sh, there are no issues with nesting quotes or "backticks" when in the form of $():

prefix="$(cd "$(dirname "$(realpath "$(which "$0")")")" && pwd)"

(my favorite cross platform method of an executable program finding its own path rather than the equivalent of messy /proc stuff (for a non-executable script, get rid of the which part)).

It seems that a ) resets the " as it encounters it. With a backtick, you need to start doing escapes. I.e the following will fail with a "no closing quote error":

prefix="`cd "`dirname "`realpath "`which "$0"`"`"`" && pwd`"
 
May or may not be relevant but I notice that in /bin/sh, there are no issues with nesting quotes or "backticks" when in the form of $():

prefix="$(cd "$(dirname "$(realpath "$(which "$0")")")" && pwd)"

Armed with your example I came up with this convoluted script....

Code:
cmd="pkg -r /mnt install -y $(cat <<PKG  | tr '\n' ' ')"                                                                                                     
drm-kmod                                                                                                                                                     
mc                                                                                                                                                           
PKG                                                                                                                                                         
                                                                                                                                                            
echo $cmd

I'm quite surprised that it works to be honest... Took quite a lot of trial and error...


My next task is to try and format this for a heredoc...
 
Back
Top