awk – как вывести все поля после $5?

Вопрос или проблема

У меня есть вывод, похожий на

  975  Jan/21 - 19:59:36 ### sed "/^#include_dir/a include_dir="conf.d"" /opt/db/data/efa_bauen_ni_14/postgresql.conf
  986  Jan/21 - 20:04:21 ### grep -l "^port="5" /opt/db/data/postgres/efa_bauen_ni/conf.d/*.conf | xargs sed -i "s/port = "5/port="6/"

Теперь я хочу сократить каждую строку до всего, что идет после $5, чтобы получить фактически полную команду?

Мне кажется, я мог бы сделать | awk {"print $6, $7, $8, $9, $10, $11'} … и так далее. Но это кажется слишком ненаучным, недостаточно гибким и некрасивым.

Может кто-то посоветовать, как этого добиться, или я с самого начала на неправильном пути с awk?

Вот способ вырезать все до первого  ###  с использованием POSIX awk:

awk 'match($0, / ### /) {print substr($0, RSTART+RLENGTH)}'

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

Пример данных выглядит так, как будто это из истории команд, вероятно, номер команды, дата, время, последовательность ### и затем команда. В примере данные выровнены хорошо, так что задача становится такой: “Как избавиться от первых 29 символов?”. Awk не должен быть вашим первым выбором, так как вам, вероятно, нужно сохранить места, где более одного пробела, и это означает, что разбивка на поля в awk не будет вам полезна.

cut -c 30-

это один из способов сделать это. Второй способ — это наблюдать, что здесь есть фиксированная строка ### , и что здесь нет символов # перед этим. Так что вы можете использовать регулярное выражение, чтобы сопоставить начало строки, ноль или более символов, которые не являются #, и затем удалить их. Преимущество этого подхода в том, что если данные изменятся, например, если номер команды станет таким большим, что потребуется дополнительный столбец для его хранения, не потребуется изменение в этой программе.

sed 's/^[^#]*### //'

Вы могли бы использовать gsub из awk, чтобы сделать то же самое, если вы действительно хотите использовать awk.

Можно использовать sed, при условии, что ### не появляется несколько раз в строке:

sed 's/^.* ### //'

Вывод

sed "/^#include_dir/a include_dir="conf.d"" /opt/db/data/efa_bauen_ni_14/postgresql.conf
grep -l "^port="5" /opt/db/data/postgres/efa_bauen_ni/conf.d/*.conf | xargs sed -i "s/port = "5/port="6/"

С помощью Perl в одну строку, чтобы напечатать все, что идет после 5-го столбца:

perl -lane 'print join " ", @F[4..$#F]' file

используя срез массива.

При использовании переключателя -a Perl ведет себя как awk и разбивает на пробелы по умолчанию в массиве @F. Вы также можете использовать -F, чтобы определить разделитель полей (может быть регулярным выражением).

Да, вы на неправильном пути с awk. Я имею в виду, вы можете сделать это в awk, и я покажу вам как в мгновение ока, но есть другой инструмент, cut, который предназначен именно для этого. Если вы хотите напечатать все поля с 5-го до последнего, вы можете просто сделать:

cut -d ' ' -f 5-

Опция -d ' ' сообщает cut использовать пробел в качестве разделителя, потому что cut по умолчанию использует TAB. Затем, опция -f используется, чтобы указать, какие поля напечатать, и здесь мы говорим напечатать с 5-го до конца (5-).

Теперь, если ваш файл не имеет строгой структуры, если вы можете иметь, скажем, одно или более пробелов в качестве разделителя, в этом случае awk будет лучшим выбором, но это сложнее. Вы могли бы сделать что-то вроде этого, например:

awk '{ line=$5; for(i=6;i<=NF;i++){ line=line OFS $(i)} print line}'

Но это все равно изменит количество пробелов в чем-то вроде:

$ echo "a b c d e           f   g" | awk '{ line=$5; for(i=6;i<=NF;i++){ line=line OFS $(i)} print line}'
e f g

Тогда как cut этого не сделает:

$ echo "a b c d e           f   g" | cut -d ' ' -f 5-
e           f   g

Я уверен, что вы можете найти более элегантное решение с awk, но действительно, cut — это правильный инструмент здесь.

Используя любой POSIX awk:

$ awk '{sub(/^[[:space:]]*([^[:space:]]+[[:space:]]+){5}/,"")} 1' file
sed "/^#include_dir/a include_dir="conf.d"" /opt/db/data/efa_bauen_ni_14/postgresql.conf
grep -l "^port="5" /opt/db/data/postgres/efa_bauen_ni/conf.d/*.conf | xargs sed -i "s/port = "5/port="6/"

или GNU awk для \s/\S:

$ awk "{sub(/^\s*(\S+\s+){5}/,"")} 1' file
sed "/^#include_dir/a include_dir="conf.d"" /opt/db/data/efa_bauen_ni_14/postgresql.conf
grep -l "^port="5" /opt/db/data/postgres/efa_bauen_ni/conf.d/*.conf | xargs sed -i "s/port = "5/port="6/"

или POSIX sed:

$ sed "s/^[[:space:]]*\([^[:space:]]\{1,\}[[:space:]]\{1,\}\)\{5\}//' file
sed "/^#include_dir/a include_dir="conf.d"" /opt/db/data/efa_bauen_ni_14/postgresql.conf
grep -l "^port="5" /opt/db/data/postgres/efa_bauen_ni/conf.d/*.conf | xargs sed -i "s/port = "5/port="6/"

или sed, который имеет опцию -E для поддержки ERE (например, GNU и BSD seds):

$ sed -E "s/^[[:space:]]*([^[:space:]]+[[:space:]]+){5}//' file
sed "/^#include_dir/a include_dir="conf.d"" /opt/db/data/efa_bauen_ni_14/postgresql.conf
grep -l "^port="5" /opt/db/data/postgres/efa_bauen_ni/conf.d/*.conf | xargs sed -i "s/port = "5/port="6/"

или GNU sed для -E и \s/\S:

$ sed -E "s/^\s*(\S+\s+){5}//' file
sed "/^#include_dir/a include_dir="conf.d"" /opt/db/data/efa_bauen_ni_14/postgresql.conf
grep -l "^port="5" /opt/db/data/postgres/efa_bauen_ni/conf.d/*.conf | xargs sed -i "s/port = "5/port="6/"

С grep версиями, которые поддерживают -o (для вывода совпавшей части) и -P (для регулярных выражений, похожих на Perl):

grep -Po '\s+###\s+\K.*'

Напечатает все, что следует за первым вхождением <whitespace>###<whitespace> в каждой строке.

grep -Po '^\s*(\S+\s+){5}\K.*'

Для вывода всего, что следует за первыми 5 полями, разделенными пробелом.

Более общий ответ, на случай, если кто-то имеет аналогичный вопрос с другим набором данных:

awk '{$1=$2=$3=$4=$5="";$0=$0;print}' [path/to/data_file]

Первая часть, $1=$2=$3=$4=$5="", устанавливает первые 5 полей в пустое значение. Недостаток этого в том, что awk все еще помнит, что у него были эти поля, поэтому print $0 оставит пустое место в начале выходной строки.

Поэтому вторая часть $0=$0 удаляет ведущие и завершающие пробелы.

Третья часть print $0 затем выводит новую, укороченную строку.

awk ‘{for (i=6;i<=NF;i++) printf(“%s%s”,(i==6)? “”:” “,$i); printf(“\n”); } ‘ file.txt

.

Ответ или решение

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

Теория

awk — это мощный инструмент для обработки текстовых данных в Unix-подобных системах. Он позволяет разбирать строки на поля, обрабатывать и выводить данные в соответствии с заданными условиями. Одной из задач, часто возникающих при работе с awk, является получение всех полей после определённого, в данном случае после пятого. Это может быть полезно, когда мы имеем дело с файлами или потоками данных, где первые поля содержат ненужную метаинформацию, и нас интересует только основное содержимое.

При работе с awk каждая запись по умолчанию разбивается на поля, используя пробелы или табуляцию как разделители. Поля нумеруются, начиная с единицы, и могут быть доступны с помощью символа $, например $1 для первого поля, $2 для второго и так далее. Однако, вывод большого количества полей вручную, как показано в примере (print $6, $7, ...), может быть неэффективным и громоздким.

Примеры

Вот несколько примеров, как можно решить эту задачу с использованием awk, а также других подходящих инструментов.

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

    awk '{for (i=6; i<=NF; i++) printf("%s%s", (i==6)? "":" ", $i); printf("\n");}' файл

    В этом примере происходит цикл от шестого поля до последнего (NF — это количество полей в текущей записи). Команда printf позволяет сохранить пробелы между полями.

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

    Если ваша структура данных позволяет использование фиксированных разделителей, инструмент cut может быть более подходящим:

    cut -d ' ' -f 6- файл

    Здесь -d ' ' указывает на использование пробелов как разделителя, а -f 6- указывает на вывод от шестого поля до конца строки.

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

    sed также может быть использован, особенно если фиксированная строка "### " позволяет точно определить начальную точку обрезки строки:

    sed -E 's/^[[:alnum:][:punct:]]+[[:space:]]{3}//' файл

    Этот пример удаляет первым пять слов вместе со всеми пробелами и символами, оставляя только команду, начиная со знаков ###.

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

    Для более сложных и гибких обработок можно использовать perl, который обладает мощными средствами работы с регулярными выражениями:

    perl -lane 'print join " ", @F[5..$#F]' файл

    В этом коде perl разбивает строку на массив @F и выводит все элементы, начиная с пятого индекса массива.

Применение

Понимание и применение вышеописанных методов может существенно упростить обработку сложных текстовых файлов, таких как логи, CSV-файлы, и другие данные, которые часто используются в ИТ-сфере. Эти инструменты не только позволяют быстро извлекать нужные данные, но и обрабатывать их в соответствии с требованиями задачи. Важно учитывать, что ваш выбор инструмента и подхода может зависеть от структуры данных, необходимости сохранения оригинального форматирования и доступности инструментов в вашей системе.

Таким образом, для автоматизации задачи вывода всех полей после пятого, лучше всего подходят инструменты, которые оптимально решают задачу с минимальной сложностью кода и без потери информации. awk и perl предоставляют мощные возможности манипуляции с текстом, но cut может быть проще и быстрее в случаях, где данные структурированы достаточно просто.

Оцените материал
Добавить комментарий

Капча загружается...