Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I'm relatively new to shell scripting, but have nearly completed a script which makes use of the lftp program. The part of the script I am having trouble with is when I create a long string of commands (separated by ;).

for var in something
do
    ...
    commands_to_run+="echo Result is `tail -n 1 $somefile`;"
done

What I'm noticing is that the tail program - wrapped in the backticks - is being run when the for loop is iterating, but not when I invoke the string of commands later in my script.

Unfortunately, the contents of $somefile isn't at this stage ready to be inspected. How can I get the command to execute when I need it, and not while I'm creating the string?

share|improve this question

2 Answers 2

up vote 3 down vote accepted

This one is a bit tricky. The information that Hauke has provided is correct, it's just a matter of parsing it out for your use case.

The easiest way is to use the $() syntax while escaping the $ such that the variable definition does not execute the command enclosed by the $() at the time of definition. The caveat is that the end result must then be re-evaluated (via eval) by the shell at the time of actual execution for the nested command to execute.

It's much easier to look at an example, so take this one, which should put you on the right track:

for((i=0;i<10;i++)); do 
  date +%s.%N  # Print a timestamp (in format seconds.nanoseconds)
  test="echo \$(date +%s.%N)" # Save a command to do the same
  sleep 1      # Sleep for a second
  eval "$test" # Evaluate the command saved in variable 'test'
  echo         # Print a new line before the next iteration
done

Here's sample output from the example above (trimmed to one iteration):

1398832186.133661344
1398832187.139076728

You'll notice that the second timestamp for each loop is about a second after the first. Conversely, if you perform the same test without escaping the $ in the test definition and removing the eval, the two timestamps will nearly match.

Don't get in the habit of using eval in most situations, but this is one of those where I don't know of a good way to avoid it. Hopefully this helps. Good luck!

share|improve this answer
    
Thanks a lot, I did try using $(...) as Hauke suggested but the backslash is the key. –  Ricky Apr 30 at 4:42
    
Glad it helped -- remember, though, the key here really is eval since you can do the same thing by not escaping the $ and using single quotes (') rather than double quotes (") to surround your command. –  daBeamer Apr 30 at 4:50
    
Now I've just realised, just as with Huake's suggestions, once I try use this in the lftp program the echo simply prints the command, it won't actually run it. Might have to try their mailing list for more specific help. –  Ricky Apr 30 at 4:53
    
What command is it that you're trying to execute? I was under the impression you wanted to echo a string with contents including the output of a delayed-execution nested command. –  daBeamer Apr 30 at 5:42
1  
@Ricky I would have to agree with all points from @HaukeLaging. The code as-is minus the echo isn't going to work because there's no command to eval, but rather a string. If you have a more pertinent example for us, we can try to help. –  daBeamer Apr 30 at 5:48

There are several levels of quoting. Double quotes ("...") protect whitespace and several special chars (~, &, |, ;, ...) but does not prevent parameter expansion and command substitution.

You need either single quotes (') or a backslash in front of the "dangerous" characters.

In general: You should consider using $(tail ...) instead of backticks. Backticks are the older standard but we are talking about so old that $() does not cause problems for most people. The new version is easier to read and can be nested. Let alone the formatting problems here on sx...

share|improve this answer
    
Thanks for the fast reply Hauke. Unfortunately substituting the backticks with the recommended $(...) still produces the same result - the shell executes this when my string is defined. –  Ricky Apr 30 at 2:03
    
@Ricky That were not alternative suggestions. You shall use $() but you need the single quotes anyway. –  Hauke Laging Apr 30 at 2:15
    
So no combination of those characters will achieve what I'm after? –  Ricky Apr 30 at 2:58
    
@Ricky What is so hard to understand about "You need either single quotes"? You obviously don't even give it a try. –  Hauke Laging Apr 30 at 3:08
    
Actually I did, but when I use single quotes the echo will just print everything on that line as a normal string. What is so hard about maybe providing an example? –  Ricky Apr 30 at 3:22

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.