Как обрабатывать 3 файла с помощью awk?

Итак, потратив 2 дня, я не могу решить эту проблему, и у меня почти нет времени. Это может быть очень глупый вопрос, поэтому, пожалуйста, потерпите меня. Мой awk-скрипт делает что-то вроде этого:

BEGIN{ n=50; i=n; }
FNR==NR {
            # Read file-1, which has just 1 column
            ids[$1]=int(i++/n);
            next
        }
        {
            # Read file-2 which has 4 columns
            # Do something
            next
        }
 END {...}

Это работает нормально. Но теперь я хочу расширить его, чтобы прочитать 3 файла. Скажем, вместо жесткого кодирования значения «n» мне нужно прочитать файл свойств и установить из него значение «n». Я нашел этот вопрос и попробовал что-то вроде этого:

BEGIN{ n=0; i=0; }
FNR==NR {
            # Block A
            # Try to read file-0
            next
        }
        {
            # Block B
            # Read file-1, which has just 1 column
            next
        }
        {
            # Block C
            # Read file-2 which has 4 columns
            # Do something
            next
        }
 END {...}

Но это не работает. Блок A выполняется для файла-0, я могу прочитать свойство из файлов свойств. Но блок B выполняется как для файлов файл-1, так и для файла-2. И блок C никогда не выполняется.

Может кто-нибудь, пожалуйста, помогите мне решить эту проблему? Я никогда раньше не использовал awk, и его синтаксис очень сбивает с толку. Кроме того, если кто-то может объяснить, как awk считывает ввод из разных файлов, это будет очень полезно.

Пожалуйста, дайте мне знать, если мне нужно добавить больше деталей к вопросу.


person Bhushan    schedule 14.07.2014    source источник
comment
Мне любопытно, почему вы называете 1-й файл file 0, а не file 1?   -  person Ed Morton    schedule 14.07.2014
comment
@EdMorton: В моем первом примере были файлы file-1 и file-2. В моем следующем примере мне нужно обработать файл перед, поэтому я использовал файл-0, здесь ничего особенного. Я знаю, что awk индексирует файлы, начиная с 1   -  person Bhushan    schedule 14.07.2014


Ответы (3)


Обновление: приведенное ниже решение работает, если все входные файлы не пусты, но см. @Ed Morton answer для более простого и надежного способа добавления обработки файлов.

Тем не менее, этот ответ по-прежнему дает, надеюсь, полезное объяснение некоторых awk основ и почему подход ОП не сработал.


Попробуйте следующее (обратите внимание, что я сделал индексы на основе 1, так как это делает awk):

awk '

 # Increment the current-file index, if a new file is being processed.
 FNR == 1 { ++fIndex }

 # Process current line if from 1st file.
 fIndex == 1 {
    print "file 1: " FILENAME
    next
 }

 # Process current line if from 2nd file.
 fIndex == 2 {
    print "file 2: " FILENAME
    next
 }

 # Process current line (from all remaining files).
 {
    print "file " fIndex ": " FILENAME
 }

' file-1 file-2 file-3
  • Шаблон FNR==1 истинен всякий раз, когда начинает обрабатываться новый входной файл (FNR содержит номер строки относительно входного файла).
  • Каждый раз, когда начинается обработка нового файла, fIndex увеличивается и, таким образом, отражает отсчитываемый от 1 индекс текущего входного файла. Подсказка к полезному ответу @twalberg.

    • Note that an uninitialized awk variable used in a numeric context defaults to 0, so there's no need to initialize fIndex (unless you want a different start value).
  • Затем шаблоны, такие как fIndex == 1, можно использовать для выполнения блоков строк только из определенного входного файла (при условии, что блок заканчивается на next).
  • Затем последний блок выполняется для всех входных файлов, которые не имеют файловых блоков (см. выше).

Что касается почему ваш подход не сработал:

  • Ваши 2-й и 3-й блоки потенциально выполняются безоговорочно для строк из всех входных файлов, поскольку им не предшествует шаблон (условие).

  • Таким образом, ваш 2-й блок вводится для строк из всех последующих входных файлов, а его оператор next затем предотвращает доступ к 3-му блоку.

Возможные заблуждения:

  • Возможно, вы думаете, что каждый блок функционирует как цикл, обрабатывающий один входной файл. awk работает НЕ так. Вместо этого вся awk программа обрабатывается в цикле, при этом каждая итерация обрабатывает одну входную строку, начиная со всех строк из файла 1, затем из файла 2, .. .

  • Программа awk может иметь любое количество блоков (обычно им предшествуют шаблоны), и то, выполняются ли они для текущей входной строки, определяется исключительно тем, является ли шаблон истинным; если шаблона нет, блок выполняется безоговорочно (для всех входных файлов). Однако, как вы уже обнаружили, next внутри блока можно использовать для пропуска последующих блоков (пар шаблон-блок).

person mklement0    schedule 14.07.2014
comment
Работает по мере необходимости. Большое спасибо за ответ и еще раз спасибо за объяснение. +1 - person Bhushan; 14.07.2014
comment
@Bhushan: я рад это слышать; Не за что. - person mklement0; 14.07.2014
comment
Это не удается, когда файл перед последним пуст, я бы вообще этого не делал, поскольку есть более надежные решения. Кроме того, очень часто при обработке нескольких файлов они имеют несколько полей, например. первый содержит сопоставление значений с двумя полями, затем следующий содержит текст N-поля, в котором некоторые поля должны быть сопоставлены в соответствии с исходным файлом. - person Ed Morton; 14.07.2014
comment
Я бы сказал, однако, что обычный тест, который большинство из нас использует, когда у нас есть только 2 файла (т.е. NR==FNR), также терпит неудачу, когда первый файл пуст, поэтому было бы не возмутительно просто указать это в качестве предостережения! - person Ed Morton; 14.07.2014

Если у вас есть gawk, просто протестируйте ARGIND:

awk '
ARGIND == 1 { do file 1 stuff; next }
ARGIND == 2 { do file 2 stuff; next }
' file1 file2

Если у вас нет gawk, получите его.

В других awks вы можете просто проверить имя файла:

awk '
FILENAME == ARGV[1] { do file 1 stuff; next }
FILENAME == ARGV[2] { do file 2 stuff; next }
' file1 file2

Это не работает, только если вы хотите дважды проанализировать один и тот же файл, в этом случае вам нужно добавить количество раз, когда этот файл был открыт.

person Ed Morton    schedule 14.07.2014
comment
Спасибо за ответ. В данный момент я не использую gawk, но код выглядит достаточно простым, чтобы его можно было понять и запомнить, буду иметь это в виду. - person Bhushan; 14.07.2014

Возможно, вам нужно подумать о добавлении некоторой дополнительной структуры, подобной этой:

BEGIN { file_number=1 }
FNR==1 { ++file_number }
file_number==3 && /something_else/ { ...}
person twalberg    schedule 14.07.2014
comment
+1 за FNR==1 { ++file_number } (что, конечно, проще, чем пытаться обнаружить изменения FILENAME). Однако, поскольку вы инициализируете 1, вы фактически начинаете с индекса 2. Мне кажется, вы могли бы вообще отказаться от блока BEGIN. - person mklement0; 14.07.2014
comment
@ mklement0 Вы правы в том, что блок BEGIN не является строго необходимым в этом случае, но с этой структурой пользователь может использовать индексы, основанные на 0 (или индексы, основанные на 42), если они хотят, поэтому я намеренно оставил его для гибкость... - person twalberg; 14.07.2014
comment
Это не удается, когда файл перед последним пуст, я бы вообще этого не делал, поскольку есть более надежные решения. - person Ed Morton; 14.07.2014