1

I am trying to modify a number of environmental variables containing predefined compiler flags. To do so, I tried using a bash loop that goes over all environmental variables listed with "env".

for i in $(env | grep ipo | awk 'BEGIN {FS="="} ; { print $1 } ' ) 

 do echo $(sed -e "s/-ipo/ / ; s/-axAVX/ /" <<< $i)  

done

This is not working since the loop variable $i contains just the name of the environmental variable stored as a character string. I tried searching a method to convert a string into a variable but things started becoming unnecessary complicated. The basic problem is how to properly supply the environmental variable itself to sed.

Any ideas how to properly modify my script are welcome.

Thanks, Alex

Alexander Cska
  • 738
  • 1
  • 7
  • 29

4 Answers4

3

Part I

The way you're parsing env is wrong. It breaks whenever you have spaces or wildcards. Instead use this:

while IFS= read -r line; do
    # do stuff with variable line
done < <(env)

To see why your solution is broken, do:

for i in $(env); do
    echo "$i"
done

and you'll very likely see a difference with the output of env.

Now the while method I gave will also break when you have newlines in your variables.

Very likely your env version has the flag -0 or --null. Use it to be 100% safe:

while IFS= read -r -d '' line; do
    # do stuff with variable line
done < <(env --null)

Part II

When you have read your line, you want to split it into a key and a value. Don't use awk for that. Use Bash:

key=${line%%=*}
value=${line#*=}

Look:

while IFS= read -r -d '' line; do
    key=${line%%=*}
    value=${line#*=}
    echo "key: $key"
    echo "value: $value"
done < <(env --null)

Part III

Now I understand that you want to act only on the variables that contain the string ipo, and for these you want to substitute a space for the first occurence of the string -ipo and -axAVX. So:

while IFS= read -r -d '' line; do
    key=${line%%=*}
    value=${line#*=}
    [[ $value = *ipo* ]] || continue
    value=${value/-ipo/ }
    value=${value/-axAVX/ }
    echo "key: $key"
    echo "new value: $value"
done < <(env --null)

Part IV

You want to replace the variable with this new value. You can use declare for this. (You don't need the export builtin, since your variable is already marked as exported):

while IFS= read -r -d '' line; do
    key=${line%%=*}
    value=${line#*=}
    [[ $value = *ipo* ]] || continue
    value=${value/-ipo/ }
    value=${value/-axAVX/ }
    declare "$key=$value"
done < <(env --null)

Part V

Finally, you'll try to put this in a script and you'll realize that it doesn't work: that's because a script is executed in a child process and every changes made in a child process are not seen by the parent process. So you'll want to source it! To source a file file, use:

. file

(yes, a dot, a space and the name of the file).

gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
  • Thank you for your extensive and well structured reply. I was basically having problems with `export ${i}=${val}`. I changed this to `declare "${i}=${val}"` and this solved the problem. Indeed the script file should be "sourced". In my case this is taken care of, since my script is in `.bashrc`. And finally indeed using awk was not that much elegant, but since I have no variables split on multiple lines defining special delimiters was unnecessary. – Alexander Cska Jun 12 '14 at 13:35
  • @AlexanderCska that's your choice to keep a broken method, whereas the 100% safe method isn't much more difficult! – gniourf_gniourf Jun 12 '14 at 13:37
  • It's actually not wrong. You can't have spaces or wildcards in a variable name, and the awk call only takes the part before the first `=`. It's just too verbose: `env | grep -o '^[^=]\+'` – glenn jackman Jun 12 '14 at 15:08
  • @glennjackman I know you'll say it's convoluted (and I will gladly agree with you), but here you go for a counter-example: consider this C file: `#include int main(int argc,char **argv) { putenv("* =ipo"); system("bash"); }`, compile it, run it, and enjoy OP's method `:)`. Could put a newline in there too if you kindly ask. `;)`. – gniourf_gniourf Jun 12 '14 at 15:24
  • @glennjackman and for a non-convoluted counter-example: `export a=$'a=\nipo *'` and enjoy too. `:)`. – gniourf_gniourf Jun 12 '14 at 15:26
2

Try with indirect expansion:

for i in $(env | grep ipo | awk 'BEGIN {FS="="} ; { print $1 } ' ) 
do
    echo $(sed -e "s/-ipo/ / ; s/-axAVX/ /" <<< ${!i})  
done
Community
  • 1
  • 1
jmlemetayer
  • 4,774
  • 1
  • 32
  • 46
1

I think the bit you are missing is the ${!i} to expand the variable called whatever $i is set to..

#!/bin/sh
for i in $(env | grep ipo | awk 'BEGIN {FS="="} ; { print $1 }' )
do
 val=$(sed -e "s/-ipo/ / ; s/-axAVX/ /" <<< ${!i})
 export ${i}=${val}
 echo ${i} is now set to $val
done

... do stuff with new env variables

If you run the script it will change the environment variable for itself and anything it spawns. When it returns however you will still have the same environment you started with.

$ echo $IPOVAR
blah -ipo -axAVX end   # variable stats as this

$ sh env.sh
IPOVAR is now set to blah end  # It is changed!

$ echo $IPOVAR
blah -ipo -axAVX end   # Its still the same.
John C
  • 4,276
  • 2
  • 17
  • 28
  • Thank you for your help. Indeed the part I was missing is ${!i}. I tested your solution, unfortunately the part "export ${i}=${val}" does not work. This is actually the part when the modified variable is exported back to the system. – Alexander Cska Jun 12 '14 at 09:10
  • Yes I thought you would probably want to save it to a file and source it. Life gets tricky changing environment variables because if you change them only processes spawned from your script will see the change. – John C Jun 12 '14 at 09:12
  • 1
    Thank you for the reply and the updated script. I have no problem with sourcing the script, actually I am ding this in .bashrc so it is all fine. The problem was that `export ${i}=${val}` was not working. I changed it to `declare "${i}=${val}"`,as gniourf_gniourf suggested, and now it runs fine. The actual error was `-bash: export: -xSSE4.2: not a valid identifier`. – Alexander Cska Jun 12 '14 at 13:21
0

I believe you can do it all in awk:

env | grep ipo | awk -F= '{ gsub("-ipo","",$2); gsub("-axAVX","",$2); print $0}'
Tiago Lopo
  • 7,619
  • 1
  • 30
  • 51
  • Hi, thanks for the answer. It is an elegant proposal unfortunately I still have to update the variables themselves. So I need to add some extra commands and sooner or later I'll end up using a bash loop. – Alexander Cska Jun 13 '14 at 12:21
  • This will only print, if you need to actually modify them use `eval $(env | grep ipo | awk -F= '{ gsub("-ipo","",$2); gsub("-axAVX","",$2); print $0}')` it should work if there is no funny characters or anything which would upset bash – Tiago Lopo Jun 13 '14 at 14:12