Compare a .txt and .csv file and need to replace w

2019-09-14 13:50发布

问题:

file1.txt

[fields:WinSpc:defect]
a=b
b=c
hello=hi

[fields:ROCKET PROJECT:ticket]
description=Descrtiption
status=status

[fields:PROJECT_Nexus:defect]
title=summary
priority=Priority_hello

file2.csv

WinSpc,projects.winspc
ROCKET PROJECT,projects.rocket_project
PROJECT_Nexus,projects.project-nexus

I need to match these two files and desired output would be :

output.txt

[fields:winspc:defect]
a=b
b=c
hello=hi
[fields:rocket_project:ticket]
description=Descrtiption
status=status
[fields:project-nexus:defect]
title=summary
priority=Priority_hello

Just the name should be changed,

i have tried using

grep -Fwf, diff --breif, 

and awk options but not getting desired output. Still learning these things. Any suggestions would be very helpful. Thanks in advance.

回答1:

A more scalable Awk logic can be done something as below.

Re-affirming the requirement for future readers, the .csv file has a field,replacement-of-field1 pair stored in multiple lines. For all those field in .csv the corresponding entries in .txt file should be replaced with replacement-of-field


1. replcement-of-field actually involves only the part after the dot

The below command does the job as intended.

awk 'FNR==NR{split($2,list,"."); replacement[$1]=list[2]; next} \
   {for (i in replacement){ if (match($0,i)) {gsub(i,replacement[i],$0); break} }}1 ' \
      FS="," file2.csv file1.txt

produces an output as OP needed,

[fields:winspc:defect]
a=b
b=c
hello=hi
[fields:rocket_project:ticket]
description=Descrtiption
status=status
[fields:project-nexus:defect]
title=summary
priority=Priority_hello

Throwing in a bit of explanation,

  1. FNR==NR logic ensures the command after it within {} is run first for the .csv file. Note that .csv file is read with field-separator ,
  2. split($2,list,".");replacement[$1]=list[2]; next ensures that the second column of the file is split by . and a hash-map is created with index set to value to be replaced and the value as actual value to be replaced. This is done for all the lines in the .csv file
  3. Now on the .txt file, for each line is checked to see if the value to be replaced is present, if present it is replaced with the replacement value.


回答2:

A sed one-liner:

sed 's#,projects.#/#;s#.*#/fields/s/&/\;#' file2.csv | sed -f - file1.txt

How it works:

  1. Transform file2.csv into sed substitute commands. So the initial code
    sed 's#,projects.#/#;s#.*#/fields/s/&/\;#' file2.csv
    outputs:

    /fields/s/WinSpc/winspc/;
    /fields/s/ROCKET PROJECT/rocket_project/;
    /fields/s/PROJECT_Nexus/project-nexus/;
    
  2. Run the resulting substitute commands on file1.txt.

    Output:

    [fields:winspc:defect]
    a=b
    b=c
    hello=hi
    [fields:rocket_project:ticket]
    description=Descrtiption
    status=status
    [fields:project-nexus:defect]
    title=summary
    priority=Priority_hello
    


回答3:

Taken into consideration your comments, this looks an exercise to replace values that follows fields: in txt file, using replacement values from another file where the "fields" value is a kind of key.

Have a look in this approach:

$ readarray -t a < <(grep -e "\[fields:" a.txt |cut -d: -f2)
$ for ((i=0;i<${#a[@]};i++));do a[i]=s/${a[i]}/$(grep -e "${a[i]}" b.txt |cut -d, -f2 |cut -d. -f2)/g\;;done
$ sed -f <(echo "${a[@]}") a.txt

Output:

[fields:winspc:defect]
a=b
b=c
hello=hi
[fields:rocket_project:ticket]
description=Descrtiption
status=status
[fields:project-nexus:defect]
title=summary
priority=Priority_hello

Explanation:

# grep the first file a.txt for all the fields: and keep the second part, i.e WinScp . Store all those findings in an array
$ readarray -t a < <(grep -e "\[fields:" a.txt |cut -d: -f2)
$ declare -p a #print the array to see what is inside
#Bash Output: declare -a a=([0]="WinSpc" [1]="ROCKET PROJECT" [2]="PROJECT_Nexus")

# Iterate through the array and with the stored value (i.e WinSpc) grep the second file 
# (b.txt in my test) and get the second field after comma. Store changes in the same array.
$ for ((i=0;i<${#a[@]};i++));do a[i]=s/${a[i]}/$(grep -e "${a[i]}" b.txt |cut -d, -f2 |cut -d. -f2)/g\;;done
$ declare -p a #print the array again. Now array looks like a sed pattern.
# Bash Output: declare -a a=([0]="s/WinSpc/winspc/g;" [1]="s/ROCKET PROJECT/rocket_project/g;" [2]="s/PROJECT_Nexus/project-nexus/g;")

# We can then apply all the sed patterns stored in array to replace values of text file (a.txt)
$ sed -f <(echo "${a[@]}") a.txt

Other solutions with AWK, etc may provide a more efficient code .