Вопрос или проблема
У меня есть вывод, похожий на
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
, а также других подходящих инструментов.
-
Использование
awk
:awk '{for (i=6; i<=NF; i++) printf("%s%s", (i==6)? "":" ", $i); printf("\n");}' файл
В этом примере происходит цикл от шестого поля до последнего (
NF
— это количество полей в текущей записи). Командаprintf
позволяет сохранить пробелы между полями. -
Использование
cut
:Если ваша структура данных позволяет использование фиксированных разделителей, инструмент
cut
может быть более подходящим:cut -d ' ' -f 6- файл
Здесь
-d ' '
указывает на использование пробелов как разделителя, а-f 6-
указывает на вывод от шестого поля до конца строки. -
Использование
sed
:sed
также может быть использован, особенно если фиксированная строка "### " позволяет точно определить начальную точку обрезки строки:sed -E 's/^[[:alnum:][:punct:]]+[[:space:]]{3}//' файл
Этот пример удаляет первым пять слов вместе со всеми пробелами и символами, оставляя только команду, начиная со знаков
###
. -
Использование
perl
:Для более сложных и гибких обработок можно использовать
perl
, который обладает мощными средствами работы с регулярными выражениями:perl -lane 'print join " ", @F[5..$#F]' файл
В этом коде
perl
разбивает строку на массив@F
и выводит все элементы, начиная с пятого индекса массива.
Применение
Понимание и применение вышеописанных методов может существенно упростить обработку сложных текстовых файлов, таких как логи, CSV-файлы, и другие данные, которые часто используются в ИТ-сфере. Эти инструменты не только позволяют быстро извлекать нужные данные, но и обрабатывать их в соответствии с требованиями задачи. Важно учитывать, что ваш выбор инструмента и подхода может зависеть от структуры данных, необходимости сохранения оригинального форматирования и доступности инструментов в вашей системе.
Таким образом, для автоматизации задачи вывода всех полей после пятого, лучше всего подходят инструменты, которые оптимально решают задачу с минимальной сложностью кода и без потери информации. awk
и perl
предоставляют мощные возможности манипуляции с текстом, но cut
может быть проще и быстрее в случаях, где данные структурированы достаточно просто.