2

I need to generate a script from within a script but am having problems because some of the commands going into the new script are being interpreted rather than written to the new file. For example i want to create a file called start.sh in it I want to set a variable to the current IP address:

echo "localip=$(ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1  -d'/')" > /start.sh

what gets written to the file is:

localip=192.168.1.78

But what i wanted was the following text in the new file:

localip=$(ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1  -d'/')"

so that the IP is determined when the generated script is run.

What am i doing wrong ?

5
  • 1
    By the way -- generally speaking, any kind of grep | tail | awk | cut sequence means you're doing it wrong. awk can do the work of grep, tail and cut, and much more efficiently. Commented Aug 18, 2015 at 16:15
  • 1
    I'd also strongly suggest using ip -o addr instead of bare ip addr -- that way you get everything on one line. Commented Aug 18, 2015 at 16:16
  • 1
    ...then again, if your shell is bash, you can also pick the IP address out of the ip -o addr output with built-in regex support, thus not needing awk, tail, cut or grep at all. Commented Aug 18, 2015 at 16:17
  • ...that said, I don't see state in my ip addr results at all; sure you aren't conflating it with ip link? Commented Aug 18, 2015 at 16:20
  • Why do you "need to generate a script from within a script"? Commented Aug 18, 2015 at 16:25

1 Answer 1

7

You're making this unnecessary hard. Use a heredoc with a quoted sigil to pass literal contents through without any kind of expansion:

cat >/start.sh <<'EOF'
localip=$(ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1  -d'/')
EOF

Using <<'EOF' or <<\EOF, as opposed to just <<EOF, is essential; the latter will perform expansion just as your original code does.


If anything you're writing to start.sh needs to be based on current variables, by the way, be sure to use printf %q to safely escape their contents. For instance, to set your current $1, $2, etc. to be active during start.sh execution:

# open start.sh for output on FD 3
exec 3>/start.sh

# build a shell-escaped version of your argument list
printf -v argv_str '%q ' "$@"

# add to the file we previously opened a command to set the current arguments to that list
printf 'set -- %s\n' "$argv_str" >&3

# pass another variable through safely, just to be sure we demonstrate how:
printf 'foo=%q\n' "$foo" >&3

# ...go ahead and add your other contents...
cat >&3 <<'EOF'
# ...put constant parts of start.sh here, which can use $1, $2, etc.
EOF

# close the file
exec 3>&-

This is far more efficient than using >>/start.sh on every line that needs to append: Using exec 3>file and then >&3 only opens the file once, rather than opening it once per command that generates output.

Sign up to request clarification or add additional context in comments.

5 Comments

Great answer. Could you elaborate on the difference between <<'EOF' and <<EOF?
@MicahSmith, as I say in the text, <<EOF runs expansions, so $(foo) gets replaced with the output of running foo. <<'EOF' keeps contents literal.
@MicahSmith, btw, to speak to why all the printf %q is necessary -- think of what happens if a variable you want to pass through contains $(rm -rf /) -- you want to be very, very sure that stays data and can't get parsed as code. :)
Thanks All .... cat with the <<'EOF' solves the issue perfectly. i'll try to keep it simple from now on ;-)
One of the things that makes bash tricky is that there's a difference between looking simple and being simple. I mean, echo $foo looks simple, but what it actually does under the hood is a lot more complex than echo "$foo"... and that's one of the most trivial examples available.

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.