Для каждой строки в файле печатать поля из определенного столбца в NF, если эти значения меньше значения в другом поле

Вот еще простой sed:

sed '/#nvram = \[/,/#]/ s/#]/#]\nfoobar/' file

Он находит строку, в которой встречается #nvram = [, а затем все строки после нее и до тех пор, пока не будет адресовано первое вхождение #], затем вы заменяете #]\nfoobarна #].

1
08.09.2021, 23:02
2 ответа

Это было протестировано с GNU Awk 5.1.0, API :3.0. Из-за использования 4-го аргумента в splitэто решение может не работать в других версиях, несовместимых с используемым здесь синтаксисом.

awk '{n=split($0, a, " ", b); line=""; for (i = 1; i <= n; i++) { if (i < 5 || a[i] < $4) line=(line a[i] b[i])}; print line; }' file.txt

Пояснение:

  • n=split($0, a, " ", b);-это разбивает всю строку($0)на значения (, хранящиеся в a), и пробелы (, хранящиеся в b), поэтому мы можем попытаться сохранить форматирование исходного файла. Значение, хранящееся в n, дает нам количество полей для обработки каждой строки. splitмассивы aи bиндексы начинаются с 1.
  • line=""-начать со строки как пустую строку
  • for (i = 1; i <= n; i++)-позволяет выполнить итерацию по каждому полю, разделение начинается с индекса 1, так же как и наш цикл.Часть <=обеспечивает обработку последнего (n-го )поля
  • if (i < 5 || a[i] < $4)-условие истинно для первых 4 полей или всякий раз, когда значение поля меньше четвертого поля (требуемое условие)
  • line=(line a[i] b[i])-объединить фактические поля и пробелы с предыдущими, которые удовлетворяют требованиям условия «если»
  • print line-выводит lineпеременную, содержащую желаемый результат
1
09.09.2021, 07:56

Если вас не волнуют пробелы в выводе, все, что вам нужно, это:

$ cat tst.awk
{
    out = $1 OFS $2 OFS $3 OFS $4
    for (i=5; i<=NF; i++) {
        if ( $i < $4 ) {
            out = out OFS $i
        }
    }
    print out
}

$ awk -f tst.awk file
NC_000001.11_NM_001005484.2 69270 234 69037 65565
NC_000001.11_NM_001005484.2 69511 475 69037 65565
NC_000001.11_NM_001005484.2 69761 725 69037 65565
NC_000001.11_NM_001385640.1 942155 20 942136 924432 925922 930155 931039 935772 939040 939272 941144

, который вы можете подключить к columnдля визуального выравнивания, если хотите:

$ awk -f tst.awk file | column -t
NC_000001.11_NM_001005484.2  69270   234  69037   65565
NC_000001.11_NM_001005484.2  69511   475  69037   65565
NC_000001.11_NM_001005484.2  69761   725  69037   65565
NC_000001.11_NM_001385640.1  942155  20   942136  924432  925922  930155  931039  935772  939040  939272  941144

В противном случае, если вы хотите, чтобы интервал в выводе выглядел так же, как интервал во вводе (, т. е. как 1 или более пробелов для первых 4 полей и 2 или более для остальных полей )и предполагая, что в некоторых строках может быть только 4 или меньше полей, а затем использовать любой POSIX awk (для классов символов и интервалов регулярных выражений):

$ cat tst.awk
BEGIN { OFS="\t" }
match($0,/([^[:space:]]+[[:space:]]+){3}[^[:space:]]+/) {
    out = substr($0,RSTART,RLENGTH)
    for (i=5; i<=NF; i++) {
        if ( $i < $4 ) {
            out = out OFS $i
        }
    }
    $0 = out
}
{ print }

Если поля после $4 должны быть разделены табуляцией -:

$ awk -f tst.awk file
NC_000001.11_NM_001005484.2 69270   234 69037   65565
NC_000001.11_NM_001005484.2 69511   475 69037   65565
NC_000001.11_NM_001005484.2 69761   725 69037   65565
NC_000001.11_NM_001385640.1 942155  20  942136  924432  925922  930155  931039  935772  939040  939272  941144

или если они должны быть разделены пробелами:

$ awk -f tst.awk file | column -s$'\t' -t
NC_000001.11_NM_001005484.2 69270   234 69037   65565
NC_000001.11_NM_001005484.2 69511   475 69037   65565
NC_000001.11_NM_001005484.2 69761   725 69037   65565
NC_000001.11_NM_001385640.1 942155  20  942136  924432  925922  930155  931039  935772  939040  939272  941144

В приведенном выше примере сохраняется пробел между первыми 4 полями, так что это будет любая комбинация табуляций и/или пробелов, которые у вас есть при вводе, а затем печатается табуляция перед каждым 5-м и последующим полем, которое вы можете использовать column, чтобы при желании заменить на эквивалентные пробелы, оба из которых выглядят как ввод и вывод в вашем вопросе.

Я создаю новую строку с именем outв циклах выше и назначаю ее $0один раз после циклов, а не изменяю $0или $iвнутри циклов, потому что каждый раз, когда вы меняете $iawk должен повторно -строить $0из своих полей, и каждый раз, когда вы меняете $0, awk должен повторно разбивать $0на поля, поэтому и то, и другое неэффективно и может привести к непредвиденным ошибкам в зависимости от содержимого полей и поэтому вы не должны изменять $0или $iвнутри цикла, если только у вас нет конкретной цели, которая требует от вас этого.

5
09.09.2021, 13:31

Теги

Похожие вопросы