简体   繁体   中英

Modify bash variables with sed

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

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).

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

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.

我相信您可以在awk中完成所有操作:

env | grep ipo | awk -F= '{ gsub("-ipo","",$2); gsub("-axAVX","",$2); print $0}'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM