Объединение определенных столбцов нескольких файлов с разделителями табуляцией на основе первого столбца

1-й столбец в inFile содержит строку, которая не обязательно присутствует во всех inFile

2-й и 7-й столбцы в каждом inFile содержат строки Title#

Используя AWK, я не могу правильно собрать это воедино. Надеюсь, использование описательных переменных поможет прояснить, что я пытаюсь сделать. Вот компоненты, которые, как мне кажется, мне нужны:

  1. входные файлы, разделенные табуляцией: -F'\t'
  2. увеличьте строки в 1-м столбце, но добавьте каждое «имя» только один раз в «1stColumnNames»: !1stColumnNames[$1]++ { name[++i] = $1 }
  3. создайте новый индекс для каждого файла .tsv, чтобы хранить значения для каждого файла, чтобы избежать перезаписи значений каждого столбца: !r[FILENAME]++ { ++argind }
  4. сохранить соответствующие значения столбцов во 2-м и 7-м столбцах для каждого файла: { 2ndColumnVals[$1, argind] = $2 } { 7thColumnVals[$1, argind] = $7 }
  5. распечатать все 1stColumnNames со связанными 2ndColumnVals и 7thColumnVals, включая их заголовки «Title1», «Title2», «Title3» и т. д.: ?????
  6. значения индекса, которые были пустыми для определенного 2ndColumnVals или 7thColumnVals, печатаются как Mtee: ?????
  7. сделайте это для всех файлов .tsv в текущем рабочем каталоге и выведите новый файл tsv: *.tsv > outFile.tsv

Примеры файлов

inFile1.tsv

Names   Title1  Title2
AAAA    1111    123456
BBBBB   1111    123456
CCC     1111    123456

inFile2.tsv

Names   Title3  Title4
BBBBB   2222    789456
DDDDD   2222    789456
EEEE    2222    789456

inFile3.tsv

Names   Title5  Title6
AAAA    3333    987654
CCC     3333    987654
EEEE    3333    987654

outFile123.tsv

Names   Title1  Title2  Title3  Title4  Title5  Title6
AAAA    1111    123456  Mtee    Mtee    3333    987654  
BBBBB   1111    123456  2222    789456  Mtee    Mtee
CCC     1111    123456  Mtee    Mtee    3333    987654
DDDDD   Mtee    Mtee    2222    789456  Mtee    Mtee
EEEE    Mtee    Mtee    2222    789456  3333    987654




Тестовый сценарий

GNU Awk 4.0.1 находится в /usr/bin/awk, поэтому я создал этот файл и запустил его в том же рабочем каталоге, где расположены 3 входных файла:

named as script1.sh
#### Example Usage:  script1.sh inFile1.tsv inFile2.tsv inFile3.tsv > outFile123.tsv

awk -F'\t' '
FNR==1 { ++numFiles}
!seen[$1]++ { keys[++numKeys] = $1 }
{ a[$1,numFiles] = $2 FS $3 }
END {
    for (keyNr=1; keyNr<=numKeys; keyNr++) {
        key = keys[keyNr]
        printf "%s", key
        for (fileNr=1;fileNr<=numFiles;fileNr++) {
            printf "\t%s", ((key,fileNr) in a ? a[key,fileNr] : "Mtee\tMtee")
        }
        print ""
    }
}
' "$@"

Запуск awk -F script1.awk inFile1.tsv inFile2.tsv inFile3.tsv > outFile123.tsv выводит следующие сообщения об ошибках:

awk: cmd. line:1: inFile1.tsv

awk: cmd. line:1: ^ syntax error




Тестовый скрипт 2 из konsolebox

работает отлично, но я пытаюсь понять каждую строку, комментируя:

#!/usr/bin/awk -f
#### named as script2.awk
#### Example Usage:  awk -f script2.awk inFile1.tsv inFile2.tsv inFile3.tsv > outFile123.tsv

BEGIN { FS = "\t" } #input File Style is tab-delimited
{ sub(/\r/, "") }   #remove all carriage return characters
!f[FILENAME]++ { ++indx }   #for all files inputted make a single index called indx
!a[$1]++ { keys[i++] = $1 } #the new indx comprises only unique strings in column 1
{ b[$1, indx] = $2 FS $3 }  #the 2nd and 3rd column are tab delimited and each pair that corresponds to a string saved in keys gets stored after the 1st column string in matrix b
END {
    for (i = 0; i in keys; ++i) {   #????
        key = keys[i]   #????
        printf "%s", keys   #prints out all strings in the index column 1 stored as keys
        for (j = 1; j <= indx; ++j) {   #????
            v = b[key, j]   #????
            printf "\t%s", length(v) ? v : "Mtee" FS "Mtee" #print out strings as tab delimited and replace any lengths of 1 char to two Mtee separated by a tab
        }
        print ""    #????
    }
}

person BlacquenedRed    schedule 30.07.2014    source источник
comment
+1 за четкое описание, проверяемый ввод/вывод и за то, что вы приложили усилия для его решения самостоятельно!   -  person Ed Morton    schedule 31.07.2014
comment
script1.awk НЕ является сценарием awk, это сценарий оболочки, который вызывает awk. Переименуйте его в script1.sh для ясности, сделайте его исполняемым, как и любой другой сценарий оболочки, добавьте "$@" в конец сценария awk, чтобы оболочка передала awk свой список аргументов, а затем выполните его как script1.sh inFile1.tsv inFile2.tsv inFile3.tsv > outFile123.tsv. Я отредактировал ваш вопрос, чтобы показать это.   -  person Ed Morton    schedule 31.07.2014


Ответы (2)


А вот еще awk:

#!/usr/bin/awk -f
# Set field separator to tab (\t)
BEGIN { FS = "\t" }
# Remove carriage return characters if file is in DOS format (CRLF)
{ sub(/\r/, "") }
# Increment indx by 1 (starts at 0) everytime a new file is processed
!f[FILENAME]++ { ++indx }
# Add a key ($1) to keys array every time it is first encountered
!a[$1]++ { keys[i++] = $1 }
# Store the 2nd and 3rd field to b matrix
{ b[$1, indx] = $2 FS $3 }
# This block runs after all files are processed
END {
    # Traverse the keys in order
    for (i = 0; i in keys; ++i) {
        key = keys[i]
        # Print key
        printf "%s", key
        # Print columns from every file in order
        for (j = 1; j <= indx; ++j) {
            v = b[key, j]
            printf "\t%s", length(v) ? v : "Mtee" FS "Mtee"
        }
        # End the line with a newline
        print ""
    }
}

Использование:

awk -f script.awk file1 file2 file3

Вывод:

Names   Title1  Title2  Title3  Title4  Title5  Title6
AAAA    1111    123456  Mtee    Mtee    3333    987654
BBBBB   1111    123456  2222    789456  Mtee    Mtee
CCC     1111    123456  Mtee    Mtee    3333    987654
DDDDD   Mtee    Mtee    2222    789456  Mtee    Mtee
EEEE    Mtee    Mtee    2222    789456  3333    987654
person konsolebox    schedule 30.07.2014
comment
Вау, спасибо! Использование особенно полезно при устранении моей ошибки при запуске скрипта. - person BlacquenedRed; 31.07.2014
comment
@BlacquenedRed Это другой ответ. - person konsolebox; 31.07.2014
comment
Это прекрасно работает, но я пытаюсь это понять. «b» - это созданная вами матрица, а индекс внутри разделен столбцами 2 и столбцами 3 с разделителями табуляции? - person BlacquenedRed; 31.07.2014
comment
indx - это счетчик, который увеличивается для каждого файла, поэтому он начинается с 1 в файле, затем переходит к 2 в файле2 и так далее. - person konsolebox; 31.07.2014
comment
Есть ли лучший ресурс, чем gnu.org/software/gawk/manual/ gawk.html#SEC_Contents ? Я добавил ваш выше как script2.awk и попытался прокомментировать то, что я понимаю, но не уверен - person BlacquenedRed; 31.07.2014
comment
@BlacquenedRed Это моя лучшая рекомендация. Я не пробовал никакой другой материал для awk. Краткий справочник также man gawk. Я делаю /something в начале, чтобы быстро найти что-то. - person konsolebox; 31.07.2014
comment
В (i = 0; i in keys; ++i) цикл начинается с i, установленного на 0. Цикл продолжается, пока действует i in keys. i in keys допустим, если ключи имеют индекс, установленный со значением i, например. это действительно, если установлены i = 1 и keys[1]. ++i увеличивает i на 1 перед перезапуском цикла. - person konsolebox; 31.07.2014

Вам нужно что-то вроде этого:

Версия Gawk (для ARGIND плюс настоящие двумерные массивы в gawk 4.0+):

$ gawk -F'\t' '
!seen[$1]++ { keys[++numKeys] = $1 }
{ a[$1][ARGIND] = $2 FS $3 }
END {
    for (keyNr = 1; keyNr <= numKeys; keyNr++) {
        key = keys[keyNr]
        printf "%s", key
        for (fileNr = 1; fileNr <= ARGIND; fileNr++) {
            printf "\t%s", (fileNr in a[key] ? a[key][fileNr] : "Mtee\tMtee")
        }
        print ""
    }
}
' file1 file2 file3

Версия без гавка:

awk -F'\t' '
FNR==1 { ++numFiles}
!seen[$1]++ { keys[++numKeys] = $1 }
{ a[$1,numFiles] = $2 FS $3 }
END {
    for (keyNr=1; keyNr<=numKeys; keyNr++) {
        key = keys[keyNr]
        printf "%s", key
        for (fileNr=1;fileNr<=numFiles;fileNr++) {
            printf "\t%s", ((key,fileNr) in a ? a[key,fileNr] : "Mtee\tMtee")
        }
        print ""
    }
}
' file1 file2 file3
Names   Title1  Title2  Title3  Title4  Title5  Title6
AAAA    1111    123456  Mtee    Mtee    3333    987654
BBBBB   1111    123456  2222    789456  Mtee    Mtee
CCC     1111    123456  Mtee    Mtee    3333    987654
DDDDD   Mtee    Mtee    2222    789456  Mtee    Mtee
EEEE    Mtee    Mtee    2222    789456  3333    987654
person Ed Morton    schedule 30.07.2014
comment
Эд, спасибо за быстрый комментарий! Однако я не могу заставить его работать должным образом, и это файл, который я сохранил: #!/usr/bin/gawk #####save and run as ./test.sh gawk -F'\t' ' ! Seen[$1]++ { keys[++numKeys] = $1 } { a[$1][ARGIND] = $2 FS $3 } END { for (keyNr = 1; keyNr ‹= numKeys; keyNr++) { key = keys[keyNr ] printf %s, ключ для (fileNr = 1; fileNr ‹= ARGIND; fileNr++) { printf \t%s, (fileNr in a[key] ? a[key][fileNr] : Mtee\tMtee) } print } } ' inFile1.tsv inFile2.tsv inFile3.tsv - person BlacquenedRed; 31.07.2014
comment
Каким образом он не работает должным образом? Неправильный вывод, отсутствие вывода, дамп ядра, сообщение об ошибке - что? Кроме того, вы не можете включать форматирование в комментарии, поэтому обновите свой вопрос, если у вас есть код или другой форматированный текст, чтобы показать нам. Наконец, запустите gawk --version и опубликуйте результат, вам понадобится gawk 4.0 или более поздней версии для настоящих 2D-массивов и многих других очень полезных функций. - person Ed Morton; 31.07.2014
comment
Ошибка в строке 1 моего файла test.sh (где находится gawk) кажется проблемой. $ который gawk возвращает местоположение /usr/bin/gawk. Черт, я только что отредактировал/обновил, прежде чем увидел, что вы изменили его на просто awk, я попробую еще раз. - person BlacquenedRed; 31.07.2014
comment
Я вижу, ты это опубликовал. Просто удалите первую строку #!/usr/bin/gawk, так как это говорит оболочке запустить gawk для сценария, но тогда первая строка (gawk -F...) является командой оболочки, а не командой awk. Вам либо нужен shebang (#!..), за которым следует ТОЛЬКО тело awk-скрипта, либо нет shebang, а затем сценарий оболочки, который вызывает awk, но вы не можете иметь оба. Лично мне обычно проще работать с подходом no shebang. - person Ed Morton; 31.07.2014