'Using awk to read from one file and inject properties into another file
I have a property file which includes hundreds of lines like the following mainfile.txt:
del.frequencyMinutes = 360
del.gracePeriodMinutes = 4320
vol.operationsEnabled = true
vol.multiFolderContainer = false
pool.capacity.differential = 30
pool.cpu.differential = 30
vol.single.pool.maxVolSize = 100
vol.multi.pool.maxVoluSize = 20
And I want to have a small file where I want to keep some changes depending on some different test setups like the following change.txt:
del.frequencyMinutes = 1440
del.gracePeriodMinutes = 2000
When properties (field $1) in change.txt is matched those in mainfile then their value should be overwritten into mainfile. I tried the following code but missing some important things:
awk -F= 'NR==FNR{val1=$1;val2=$2;next}{ if ($1==val1) printf $1" = "val2;else if ($1 != val1) print $0}' changes.txt mainfile.txt
I saw that it only injected second property row in change.txt into mainfile.txt while missing first one. So,
- how could I add all
change.txtrows - The code above only prints the results, not write into mainfile, so how could I permanently write into mainfile?
I'm not very good in awk/shell scripting so I would appreciate your explaining things what I did wrong here
Solution 1:[1]
I would use GNU AWK for this task following way
awk 'BEGIN{FS=OFS=" = "}NR==FNR{arr[$1]=$2;next}($1 in arr){$2=arr[$1]}{print}' changes.txt mainfile.txt
which for changes.txt
del.frequencyMinutes = 1440
del.gracePeriodMinutes = 2000
and mainfile.txt
del.frequencyMinutes = 360
del.gracePeriodMinutes = 4320
vol.operationsEnabled = true
vol.multiFolderContainer = false
pool.capacity.differential = 30
pool.cpu.differential = 30
vol.single.pool.maxVolSize = 100
vol.multi.pool.maxVoluSize = 20
gives output
del.frequencyMinutes = 1440
del.gracePeriodMinutes = 2000
vol.operationsEnabled = true
vol.multiFolderContainer = false
pool.capacity.differential = 30
pool.cpu.differential = 30
vol.single.pool.maxVolSize = 100
vol.multi.pool.maxVoluSize = 20
Explanation: I set both field separator (FS) and output field separator (OFS) to =, so after altering one field line would still be property = value, then when processing first file (NR==FNR) I set array arr value for key property to be its' value. next is used so no other action is undertaken. In all but first file: if property (1st field, $1) is one of arr keys, then set value (2nd field, $2) to be value for that key, always print whole line.
(tested in gawk 4.2.1)
Solution 2:[2]
You are overwriting the previous values of val1 and val2 when there is more than one line in the input file. The usual and very common solution is to read the lines into an associative array.
awk -F= 'NR==FNR { a[$1] = $0; next }
$1 in a { print a[$1]; next } 1' changes.txt mainfile.txt >temp
mv temp mainfile.txt
This prints the input from changes.txt when it sees a first field which was in that file, and otherwise prints the original input line. The results are written to a temporary file, which we then rename to move it back on top of the original mainfile.txt.
The -F = is dubious here; perhaps take it out, and/or normalize whitespace around $1 before checking if it's in a. String comparisons in Awk (or pretty much every general-purpose programming language) regards strings as different if they have different amounts of spaces around the text.
The lone 1 at the end is a common Awk shorthand for "if you got this far, just print the current input line."
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Daweo |
| Solution 2 | tripleee |
