17

I try to set variable which get interface ip-address from ifconfig and read it later. But when I execute a echo command, variable still empty. Please look at my code:

/usr/bin/bash -c "HOST_IPS=$(/usr/bin/ifconfig | /usr/bin/awk 'BEGIN {cnt=0} {if($0 ~ /inet / && cnt==1) {print $2} cnt++}'); echo $HOST_IPS"

But /bin/echo work fine with same command:

/usr/bin/bash -c "echo $(/usr/bin/ifconfig | /usr/bin/awk 'BEGIN {cnt=0} {if($0 ~ /inet / && cnt==1) {print $2} cnt++}')"
dlmeetei
  • 9,905
  • 3
  • 31
  • 38
d0xin
  • 173
  • 1
  • 4
  • 1. Where do you see `/bin/echo`? 2. Because your outer quotes are double quotes, you need to escape _all_ the `$` signs inside. – gniourf_gniourf Mar 01 '15 at 12:55

3 Answers3

20

You have to escape the $ sign in the final echo command, or the variable $HOST_IPS will be substituted into the command string before the subshell is spawned:

/usr/bin/bash -c "HOST_IPS=$(/usr/bin/ifconfig | /usr/bin/awk 'BEGIN {cnt=0} {if($0 ~ /inet / && cnt==1) {print $2} cnt++}'); echo \$HOST_IPS"

For more immediate visibility:

#                                                  v-- insert backslash here
/usr/bin/bash -c "HOST_IPS=$(same as before); echo \$HOST_IPS"

Contrary to @gniourf_gniourf's comment, it is not actually necessary to escape the other dollar signs. However, as written, the command substitution is not performed by the subshell (!); its result is substituted into the command string that is passed to the subshell. The calls

mypid() { echo $$; }
bash -c "pid=$(mypid); echo \$pid; mypid"

demonstrate the way it works: it will once print the PID of the parent shell and once complain that mypid is not a known command because the subshell does not know the function.

Since running the ifconfig | awk command in the parent shell is unlikely to be a problem, you can probably leave the command substitution part unchanged. If it is important that the command be run by the subshell, you'll have to escape all the things there as well.

Wintermute
  • 42,983
  • 5
  • 77
  • 80
  • @Wintermute You are wrong about /proc/self. [link](https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/3/html/Reference_Guide/s1-proc-directories.html) – nawK Mar 02 '15 at 17:17
  • Oh, right -- readlink spawns its own process. Hmm...how to demonstrate, then? Give me a moment. – Wintermute Mar 02 '15 at 17:21
  • @Wintermute No. You want `$$`. – nawK Mar 02 '15 at 17:25
  • I don't care about the PID, really; I just want to show that the command substitution happens before the subshell is spawned. The PID was the first thing I thought of to demonstrate it, but you're right that I fumbled there. Shell functions seem like a good way, though. – Wintermute Mar 02 '15 at 17:33
0

With /usr/bin/bash you start a subshell. When the shell is finished, all settings in the shell are lost.
The following sub is set in the subshell and lost before being echoed:

/usr/bin/bash -c sub=1; echo $sub

Do you want to set a variable in a subshell, use stdout for transporting the value:

sub=$(/usr/bin/bash -c "echo 1"); echo $sub
Walter A
  • 19,067
  • 2
  • 23
  • 43
0

From your question and example, your task at hand doesn't require you to leave your current environment, hence you don't need to start a new one and be concerned with data lost.

HOST_IPS=();
while read -r;
  do [[ $REPLY =~ "inet "([^/]*) ]] && HOST_IPS+=(${BASH_REMATCH[1]});
done < <( ip -f inet address show )

If you wish to maintain a list of interface IP-addresses you can process the output of ip (or ifconfig) in your current shell without calling awk.

nawK
  • 693
  • 1
  • 7
  • 13