Как мне упорядочить и заполнить недостающие поля от чтения двух файлов в bash - PullRequest
0 голосов
/ 19 июня 2019

У меня есть два текстовых файла

cat A.txt

10,1,1,"ABC"
10,1,2,"S1"
10,1,2,"ABC"
10,1,3,"baba"
10,2,1,"S2"
10,2,1,"asd"
10,2,2,"S3"
10,2,2,"dkkd"
10,2,3,"ABC"

cat B.txt

10,1,1,"ABC1"
10,1,2,"S1"
10,1,2,"ABC"
10,1,3,"baba"
10,2,1,"asd"
10,2,2,"S3"
10,2,2,"dkkd"
10,2,4,"bokaj"

Я хочу найти пропущенные поля, читая издва текстовых файла и заполните оба файла для пропущенных полей с помощью "" и сохраните в два новых измененных файла. Как мне это сказать

A1.txt является модифицированной версией A.txt

cat A1.txt

10,1,1,"ABC"
10,1,2,"S1"
10,1,2,"ABC"
10,1,3,"baba"
10,2,1,"S2"
10,2,1,"asd"
10,2,2,"S3"
10,2,2,"dkkd"
10,2,3,"ABC"
10,2,4,"  "

B1.txt является модифицированной версией B.txt

cat B1.txt

10,1,1,"ABC1"
10,1,2,"S1"
10,1,2,"ABC"
10,1,3,"baba"
10,2,1,"  "
10,2,1,"asd"
10,2,2,"S3"
10,2,2,"dkkd"
10,2,3,"  "
10,2,4,"bokaj"

, убедитесь, что общее количествострок в A1.txt такое же, как в B1.txt, извините, я не смог опубликовать свой MWE из-за отсутствия команд, так как я новичок в bash, ваш ответ с объяснением может помочь мне узнать это.

Это мой MWE, который я пробовал до сих пор

#!/bin/bash


cut -d ',' -f1,2,3 A.txt > A1.txt
cut -d ',' -f1,2,3 B.txt > B1.txt


## Command to print contents which are in B1.txt but not in A1.txt

A=`awk 'NR==FNR{a[$0];next} !($0 in a)' A1.txt B1.txt`
echo $A,'" "' >> A.txt
sort A.txt

## Command to print contents which are in A1.txt but not in B1.txt

B=`awk 'NR==FNR{a[$0];next} !($0 in a)' B1.txt A1.txt`
echo $B,'" "' >> B.txt
sort B.txt

Ответы [ 3 ]

2 голосов
/ 20 июня 2019

Итак:

  • сначала извлеките сопоставимую часть файлов - т.е. первые 3 поля
  • затем сортируйте их для извлечения уникальных строк в файлах
  • добавить уникальные строки из обоих файлов с суффиксом ," "
  • распечатать уникальные строки из файла A.txt с файлом B.txt, отсортировать их и поместить в файл B1.txt
  • распечатать уникальные строки из файла B.txt с файлом A.txt, отсортировать их и поместить в файл A1.txt

Следующий код:

cat <<EOF >A.txt
10,1,1,"ABC"
10,1,2,"S1"
10,1,2,"ABC"
10,1,3,"baba"
10,2,1,"S2"
10,2,1,"asd"
10,2,2,"S3"
10,2,2,"dkkd"
10,2,3,"ABC"
EOF
cat <<EOF >B.txt
10,1,1,"ABC1"
10,1,2,"S1"
10,1,2,"ABC"
10,1,3,"baba"
10,2,1,"asd"
10,2,2,"S3"
10,2,2,"dkkd"
10,2,4,"bokaj"
EOF

# extract unique lines from first and second file
# hide lines common in both files
comm -3 <(
  # extract 3 fields from A.txt and sort
  < A.txt \
  cut -d, -f1-3 |
  sort
) <(
  # extract 3 fields from B.txt and sort
  < B.txt \
  cut -d, -f1-3 |
  sort
) |
# suffix with `,"   "` string
sed 's/$/,"  "/' |
# split the stream
tee >(
  # extract lines unique to the first file, ie. A.txt file
  grep -v $'^\t' |
  # join the stream with the content of B.txt file
  # also note that lines from stdin are preferred in sorting order
  # over the same lines from B.txt file
  # sort it using first 3 fields. Preserve sorting from B.txt file
  # and put the output into B1.txt
  sort -s -t, -k1,3 - B.txt \
  > B1.txt
) |
# extract lines unique to the second file, ie. B.txt file
grep $'^\t' | cut -f2 |
# join the output with A.txt file
# sort it using first 3 fields, preserve sorting, put into A1.txt
sort -s -t, -k1,3 - A.txt \
> A1.txt

# verbose output
set -x
cat B1.txt
cat A1.txt

выведет:

++ cat B1.txt
10,1,1,"ABC1"
10,1,2,"S1"
10,1,2,"ABC"
10,1,3,"baba"
10,2,1,"  "
10,2,1,"asd"
10,2,2,"S3"
10,2,2,"dkkd"
10,2,3,"  "
10,2,4,"bokaj"
++ cat A1.txt
10,1,1,"ABC"
10,1,2,"S1"
10,1,2,"ABC"
10,1,3,"baba"
10,2,1,"S2"
10,2,1,"asd"
10,2,2,"S3"
10,2,2,"dkkd"
10,2,3,"ABC"
10,2,4,"  "

проверено на repl.it .

comm имеет странный вывод, поскольку он не имеет суффикса со строками-разделителями, уникальными для первого файла. Поэтому я научился grep составлять таблицы для извлечения уникальных строк, поэтому для получения уникальных строк из файлов A.txt или B.txt я делаю grep -v $'^\t' или grep $'^\t' | cut -d2 соответственно.

При запуске comm два раза этот скрипт может быть более многословным и более "линейным, как" (не знаю, как его назвать, без tee):

comm -13 <(
  < A.txt \
  cut -d, -f1-3 |
  sort
) <(
  < B.txt \
  cut -d, -f1-3 |
  sort
) |
sed 's/$/,"  "/' |
sort -s -t, -k1,3 - A.txt \
> A1.txt

comm -23 <(
  < A.txt \
  cut -d, -f1-3 |
  sort
) <(
  < B.txt \
  cut -d, -f1-3 |
  sort
) |
sed 's/$/,"  "/' |
sort -s -t, -k1,3 - B.txt \
> B1.txt

И немного аккуратного 4 вкладыша:

comm -3 <(cut -d, -f1-3 A.txt | sort) <(cut -d, -f1-3 B.txt | sort) |
sed 's/$/,"  "/' |
tee >(grep -v $'^\t'  | sort -s -t, -k1,3 - B.txt > B1.txt) |
grep $'^\t' | cut -f2 | sort -s -t, -k1,3 - A.txt > A1.txt
1 голос
/ 19 июня 2019

С GNU awk для массивов массивов, sorted_in, gensub () и ARGIND:

$ cat tst.awk
BEGIN { FS=OFS="," }
{ keySet[$1][$2][$3] = key = $1 FS $2 FS $3 }
ARGIND==1 {
    instNr = ++tots[key]
}
ARGIND==2 {
    instNr = ++cnt2[key]
    if (instNr > tots[key]) {
        tots[key] = instNr
    }
}
{ vals[ARGIND,key,instNr] = $NF }

END {
    PROCINFO["sorted_in"] = "@ind_num_asc"
    for (k1 in keySet) {
        for (k2 in keySet[k1]) {
            for (k3 in keySet[k1][k2]) {
                keys[++numKeys] = keySet[k1][k2][k3]
            }
        }
    }

    prt(1)
    prt(2)
}

function prt(fileNr,    fname, keyNr, key, instNr, idx, val) {
    fname = gensub(/\.[^.]+$/,"",1,ARGV[fileNr]) "1.txt"
    for (keyNr=1; keyNr<=numKeys; keyNr++) {
        key = keys[keyNr]
        for (instNr=1; instNr<=tots[key]; instNr++) {
            idx = fileNr SUBSEP key SUBSEP instNr
            if ( !(idx in vals) ) {
                print key, "\"  \"" "\t> " fname
            }
        }
        for (instNr=1; instNr<=tots[key]; instNr++) {
            idx = fileNr SUBSEP key SUBSEP instNr
            if ( idx in vals ) {
                print key, vals[idx] "\t> " fname
            }
        }
    }
}

.

$ awk -f tst.awk A.txt B.txt
10,1,1,"ABC"    > A1.txt
10,1,2,"S1"     > A1.txt
10,1,2,"ABC"    > A1.txt
10,1,3,"baba"   > A1.txt
10,2,1,"S2"     > A1.txt
10,2,1,"asd"    > A1.txt
10,2,2,"S3"     > A1.txt
10,2,2,"dkkd"   > A1.txt
10,2,3,"ABC"    > A1.txt
10,2,4,"  "     > A1.txt
10,1,1,"ABC1"   > B1.txt
10,1,2,"S1"     > B1.txt
10,1,2,"ABC"    > B1.txt
10,1,3,"baba"   > B1.txt
10,2,1,"  "     > B1.txt
10,2,1,"asd"    > B1.txt
10,2,2,"S3"     > B1.txt
10,2,2,"dkkd"   > B1.txt
10,2,3,"  "     > B1.txt
10,2,4,"bokaj"  > B1.txt

Изменить "\t> " в строке print напросто > когда вы довольны результатами, чтобы действительно записать в выходные файлы.

1 голос
/ 19 июня 2019

Вот скрипт awk, который может выполнить задачу.

script.awk

FNR == NR {        # read first input file
    fileNames[1] = FILENAME".1";
    lines[FNR",1"] = $0;
    file1[$0];
    file1count = FNR;
    next;
}
{                  # read scond input file
    fileNames[2] = FILENAME".1"; 
    lines[FNR",2"] = $0;
    file2[$0];
}
END {
    printf "" > fileNames[1]; # clean file 1
    printf "" > fileNames[2]; # clean file 2
    maxFileLen = file1count > FNR ? file1count : FNR;
    for (i = 1; i <= maxFileLen; i++) {  # for each line in file 1
        print lines[i",1"] >> fileNames[1]; # overwrite file 1
        if (lines[i",1"] in file2 == 0) print replaceField(lines[i",1"]) >> fileNames[2];
        if (lines[i",2"] in file1 == 0) print replaceField(lines[i",2"]) >> fileNames[1];
        print lines[i",2"] >> fileNames[2]; # overwrite file 2
    }
}
function replaceField(inpStr) {
    sub(/"[^"]+"/, "\"     \"", inpStr);
    return inpStr;
}

запущенный скрипт

 awk -f script.awk A.txt B.txt

Выходные файлы append .1 к имени файла

Выход A.txt.1

10,1,1,"ABC"
10,1,1,"     "
10,1,2,"S1"
10,1,2,"ABC"
10,1,3,"baba"
10,2,1,"S2"
10,2,1,"asd"
10,2,2,"S3"
10,2,2,"dkkd"
10,2,4,"     "
10,2,3,"ABC"

Выход B.txt.1

10,1,1,"     "
10,1,1,"ABC1"
10,1,2,"S1"
10,1,2,"ABC"
10,1,3,"baba"
10,2,1,"     "
10,2,1,"asd"
10,2,2,"S3"
10,2,2,"dkkd"
10,2,4,"bokaj"
10,2,3,"     "

Выход соответствует описанию проблемы.Но не образец вывода.

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...