AlikElzin-kilaka
AlikElzin-kilaka

Reputation: 36021

How to use bash script to replace keys with values in files by using a property file as the input

I have several configuration files with configurable data within them. Example (look at ${ConnectorPort}):

<Connector port="${ConnectorPort}" protocol="HTTP/1.1" server="MyServer"
           connectionTimeout="20000"
           redirectPort="8443"
           URIEncoding="UTF-8"/>

In config time, I'd like to replace the keys with the values from a given properties file.

The problem is that it takes a long time (a few seconds per file) to iterate over all the files and iterate over all the properties to check for replacement for each property. Script below.

Any idea how to do it faster using bash?

Perhaps there's a way in sed or awk to get all properties as a single input?

Perhaps do the replacement in memory?

Script:

for file in "${files[@]}"
do
    # echo ------
    #echo In file: $file
    #echo ------
    cp -f "$file" "$file.bak"
    unset replaced
    for prop in "${!props[@]}"
    do
        key="$prop"
        keyToReplace="\${$prop}"
        val="${props[$key]}"
        # echo In prop: $key=$val
        sed -i "s|$keyToReplace|$val|Igw $file.change" "$file"
        assertReturnStatus $? "sed failed"
        if [ -z "$replaced" ] && [ -s "$file.change" ]; then
            replaced=true
            echo Replacing props in file "$file"
            cp -n "$file.bak" "$file.orig"
        fi
    done
    rm -f "$file.change"
    rm -f "$file.bak"
done

* One of the requirements is to create a backup (.orig) if a file is changed.

Thanks.

Upvotes: 0

Views: 1152

Answers (2)

AlikElzin-kilaka
AlikElzin-kilaka

Reputation: 36021

An in-memory solution, reading the content to memory and doing the replacement in memory:

for file in "${files[@]}"
do
#   echo ------
#   echo In file: $file
#   echo ------
    unset replaced
    contentOrig=`cat "$file"`
    content="$contentOrig"
    for prop in "${!props[@]}"
    do
        # No need to search for all properties if there are
        if [[ ! "$content" == *'${'* ]]; then
            break
        fi
        key="$prop"
        keyToReplace="\${$prop}"
        val="${props[$key]}"
#       echo In prop: $key=$val
        content="${content//$keyToReplace/$val}"
        assertReturnStatus $? "sed failed"
    done
    # TODO: Show warning if `${` still exists (Excluding `*.sh` files). We might have forgotten to add a property.
    if [[ "$content" != "$contentOrig" ]]; then
        let "filesReplacedCount++"
        cp -n "$file" "$file.orig"
        echo "$content" > "$file"
        echo Replaced props in file "$file"
    fi
done

Upvotes: 0

RTLinuxSW
RTLinuxSW

Reputation: 842

Your loop inside the loop causes sed to run multiple times for each file. So, get rid of the loop inside the loop.

declare -a sedArgs

for prop in "${!props[@]}"
do
    key="$prop"
    keyToReplace="\${$prop}"
    val="${props[$key]}"
    # echo In prop: $key=$val
    #sed -i "s|$keyToReplace|$val|Igw $file.change" "$file"
    sedArgs+=("-e")
    sedArgs+=("s|$keyToReplace|$val|Ig")
done

for file in "${files[@]}"
do
    # echo ------
    #echo In file: "$file"
    #echo ------
    sed -i.orig "${sedArgs[@]}" $file
    cmp -s "$file" "${file}.orig" && mv "${file}.orig" "$file"

done

Upvotes: 1

Related Questions