Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash scripting: update a properties file

propertyOne=1
propertyTwo=a/b
propertyThree=three

How do I change the content of the property file to the following pattern?

  • propertyOne will add a string before the original value
  • propertyTwo will add a string in the middle
  • propertyThree will add a string at the end

    propertyOne=apple/1
    propertyTwo=a/and/b
    propertyThree=three/end
    

I tried using sed -i -e but I am only successful if I hard-code the changes for every line; any suggestions for improving the code?

sed -i -e '/propertyTwo=/ s=.*/=one/2/two' path/to/file
like image 529
Led Avatar asked Nov 20 '25 08:11

Led


1 Answers

In this case, a pure Bash solution offers both flexibility and robustness (but see further below for a faster awk solution).

While Bash solutions that read files line by line are generally slow, this probably won't be a concern with properties files, which tend to be small.

#!/usr/bin/env bash

while IFS='=' read -r prop val; do
  case $prop in
    propertyOne)
      val="apple/$val"
      ;;
    propertyTwo)
      val="${val/\///and/}"
      ;;
    propertyThree)
      val="$val/end"
      ;;
  esac
  printf '%s\n' "$prop=$val"
done < file > file.tmp && mv file.tmp file

The Bash builtin read conveniently offers rest-of-the-line logic: by only specifying 2 variables in IFS='-' read -r prop value, the 2nd variable value receives everything after the first =, whatever it is, even if it contains additional = instances.

< file > file.tmp && mv file.tmp file is a common idiom for (loosely speaking) in-place updating of a file. Technically, the modified content is written to a temp. file, and that temp. file then replaces the original.

Note:
* This indirect way of updating is needed, because the shell doesn't support reading from and outputting to the same file in the same command.
* This simple approach can be problematic, in that if the input file was a symlink, it is replaced with a regular file, the new file's permissions may be different, ...


awk, as demonstrated in karakfa's answer, is certainly the faster choice, but it comes with a caveat - which may or may not be a problem for you:

Conceptually, a properties file is not strictly field-based, because a property value may contain value-internal = instances.

If you split the input into fields by =, then generic value handling can be problematic, because you won't have a single variable referring to the value as a whole.

A quick example: Say you have an input line foo=bar=baz, and you want to append string @ to the existing value, bar=baz, without having to know ahead of time whether the existing value happens to have embedded = chars.
If you blindly use $2 = $2 "@" for appending, the resulting value will be just bar@ - in other words: you've lost data.

Solving this problem requires a little more work; here's an awk solution adapted from karakfa's, which provides the whole value in single variable val:

awk -F= '
  # Capture the entire value (everything to the right of "=") in variable "val".
  { val= $0; sub("^[^=]+=", "", val) }
  $1 == "propertyOne"   { val = "apple/" val } 
  $1 == "propertyTwo"   { sub(/\//, "/and/", val) }   
  $1 == "propertyThree" { val = val "/end" }
  { print $1 "=" val }  
' file > file.tmp && mv file.tmp file

Note: If you use GNU awk and the version number is >= 4.1, you can use -i inplace instead of > file.tmp && mv file.tmp file to update the input file in-place (loosely speaking). Beside being more convenient than the latter approach, -i inplace also preserves the original file's permissions, but the basic approach is the same: the file is replaced, which bears the risk of replacing a symlink with a regular file.


sed is not a good choice, because it's hard to limit substitutions to part of a line in a generic manner.

like image 193
mklement0 Avatar answered Nov 22 '25 22:11

mklement0



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!