2

I have two files A and B. Every line in both files is considered an item. The format of every item is fixed, consisting of a key and description, separated by a space. as shown in the example below.

UASCH-XCF02-SP062 /users/documents/ark

The first part UASCH-XCF02-SP062 is the key and the last part /users/documents/ark is the description. File A and B have 1000 and 100000 items respectively. Every key is unique in the same file, but the keys of items in file A also occurred in file B with a different description. As shown in a simple example below.

File A

UASCH-XCF02-SP062 /users/documents/ark1
UASCH-XXF02-SP063 /users/documents/ark2

File B

UASCH-XCF02-SP062 /users/documents/ark3
UASCH-XXF02-SP063 /users/documents/ark4
UASCH-XXF03-SP064 /users/documents/ark5

I want to replace the description corresponding to the same key in file B with the description corresponding to the key in file A. The result in the example is shown below.

File B

UASCH-XCF02-SP062 /users/documents/ark1
UASCH-XXF02-SP063 /users/documents/ark2
UASCH-XXF03-SP064 /users/documents/ark5

How to achieve this target?

0

3 Answers 3

7

This can be done using AWK:

$ awk 'NR==FNR{a[$1]=$2;next} $1 in a {$2=a[$1]} 1' A.txt B.txt
UASCH-XCF02-SP062 /users/documents/ark1
UASCH-XXF02-SP063 /users/documents/ark2
UASCH-XXF03-SP064 /users/documents/ark5

Edit: simplified the second half of the AWK expression thanks to Ed's comment.

To edit file B, redirect AWK output to a temporary file, and then replace file B with it, like so:

awk 'NR==FNR{a[$1]=$2;next} $1 in a {$2=a[$1]} 1' A.txt B.txt >B.txt.tmp
mv B.txt.tmp B.tmp 

How it works. We first save all of the key-value pairs of the first file (A) into an associative array, using NR==FNR idiom to distinguish the first file from the second. Then, when we're walking through the second file (B) we're checking if the current key was present in the first file, and if it was - we replace the current value with the value found in the first file.

0
0

In pure bash ... If bash is your shell, then you can do this with only shell builtins and no external utilities/commands will be needed ... It, however, might be a bit slower than dedicated text processing tools in some cases, but can be helpful to know nonetheless.

Declare an associative array array like so:

declare -A array

Then read file A into it with white-space i.e. as the internal field separator like so:

while IFS=' ' read -r k v
  do
  array[$k]="$v"
  done < A

Then read file B lines, compare keys, replace values and print end result like so:

while IFS=' ' read -r k v
  do
  if [[ "${!array[*]}" =~ "$k" ]]
  then
  printf '%s %s\n' "$k" "${array[$k]}"
  else
  printf '%s %s\n' "$k" "$v"
  fi
  done < B

Which will output:

UASCH-XCF02-SP062 /users/documents/ark1
UASCH-XXF02-SP063 /users/documents/ark2
UASCH-XXF03-SP064 /users/documents/ark5

Notice: As bash arrays are one-dimensional, the above approach will work for your use case (i.e. one key and one value) and similar but not for keys with more than one value when each extra value needs to be parsed as an individual token.

-1

Sort is stationary, and 'uniq' can be made to consider only the first N characters so ....

cat A B | sort | uniq -w 17 > /tmp/foo ; cp /tmp/foo B

Yes - it works as expected

$ cat A B | sort | uniq -w 17
UASCH-XCF02-SP062 /users/documents/ark1
UASCH-XXF02-SP063 /users/documents/ark2
UASCH-XXF03-SP064 /users/documents/ark5
2
  • 3
    No need for uniq, sort can do its job, plus it uses fields instead of not specific number of characters: sort -u -k1,1 A.txt B.txt
    – user469457
    Commented Apr 8, 2023 at 10:50
  • That's not what the OP is trying to do. Try it and you'll see it doesn't produce the expected output.
    – Ed Morton
    Commented Apr 8, 2023 at 12:49

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .