#awk
Вопрос:
У меня есть такой файл, который содержит несколько полей в строках. Я хочу отобразить некоторые из них, обрабатывая один из них другой командой.
TITLE,OpenVPN ...
HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Virtual IPv6 Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t),Username,Client ID,Peer ID
CLIENT_LIST,name1,1.1.1.1:1,10.0.0.1,,2692253,3765861,Wed Jun 23 12:51:08 2021,1624452668,name1,4727,0
CLIENT_LIST,name2,2.2.2.2:2,10.0.0.2,,1571221,2080242,Thu Jul 1 19:24:10 2021,1625167450,name2,5625,0
CLIENT_LIST,name3,3.3.3.3:3,10.0.0.3,,2670410,3736957,Wed Jun 23 16:20:51 2021,1624465251,name3,4747,0
...
Ожидаемый результат таков:
name1 10.0.0.1 2021-06-23 12:51:08
name2 10.0.0.2 2021-07-01 19:24:10
name3 10.0.0.3 2021-06-23 16:20:51
Команда, которая у меня сейчас есть, такова:
grep '^CLIENT_LIST,' /var/run/ovpn-server.status |awk -F',' '{print $2 $4 $9}' |sort
Он печатает нужные поля, но не преобразует метку времени в форматированное время. Вот команда для этого:
date -d @1624452668 "%Y-%m-%d %H:%M:%S"
Как я могу интегрировать date
команду в awk
сценарий? Или какое другое решение существует для достижения этой цели?
Я также намерен поместить выходные данные в макет столбцов/таблиц с column
помощью команды, я делал это раньше, так что это не входит в вопрос.
Комментарии:
1. Ожидаемый результат в вашем вопросе-это отформатированная дата,
$8
но примерdate
иawk
команды, которые вы опубликовали, используются$9
. В какое поле вы хотите конвертировать — $8 (напримерWed Jun 23 12:51:08 2021
) или $9 (например1624452668
)?2. На самом деле оба поля могут выдавать один и тот же результат в зависимости от вашего часового пояса. Для меня они другие.
3. Я собираюсь использовать целочисленную метку времени, для которой требуется
@
перед нейdate
. Может быть, я неправильно посчитал.4. Нет, ваш подсчет в порядке, и отметка времени эпохи составляет 9 долларов, просто результат, который увидят те из нас, кто не находится в вашем часовом поясе, будет отличаться от ожидаемого результата, который вы опубликовали. Вместо этого мы получим тот же результат, что и вы, если будем использовать 8 долларов вместо 9 долларов. Это нормально, если вы предпочитаете использовать временную метку эпохи от 9 долларов, но, делая это, вы заставляете решение либо использовать GNU awk для его
strftime()
расширения, либо работать намного медленнее, чем если бы вы просто использовали текст, уже присутствующий в 8 долларах, с любым awk.5. Скорость здесь не имеет значения, это всего лишь дюжина или около того линий. И я предпочитаю UTC для источников данных, местное время предназначено только для окончательного отображения, и этот формат времени там… беспорядок.
Ответ №1:
Вы можете использовать это awk
:
awk -F, -v OFS='t' '$1 == "CLIENT_LIST" {
cmd = "date 47%Y-%m-%d %H:%M:%S47 -d47@" $9 "47"
print $2, $4, ((cmd | getline dt) > 0 ? dt : $9)
close(dt)
}' file
name1 10.0.0.1 2021-06-23 08:51:08
name2 10.0.0.2 2021-07-01 15:24:10
name3 10.0.0.3 2021-06-23 12:20:51
Объяснение:
-F, -v OFS='t'
: Устанавливает разделитель полей ввода как,
и разделитель полей вывода как вкладку'$1 == "CLIENT_LIST"
: Сделайте это, когда первое полеCLIENT_LIST
cmd = "date 47%Y-%m-%d %H:%M:%S47 -d47@" $9 "47"
:date
Команда форматирования с использованием$9
cmd | getline dt
вызывает внешнююdate
команду(cmd | getline dt) > 0
: Когдаdate
команда успешнаprint
: печатает 2-й, 4-й и выводdate
поля
Комментарии:
1. Отлично сделано. Учет запятых всегда является забавной частью при смешивании оболочки с awk.
2. Отформатированное время, по-видимому, имеет некоторое ведущее пространство, оно не выравнивается в
column
выводе. Кроме того, я был бы признателен за некоторое объяснение, поскольку я не понимаю, что делает этот сценарий и почему он на самом деле работает. Что такое{
,cmd =
, скобки,close
иdt
?3. Я добавил объяснение в ответ. Дайте мне знать, если возникнет какой-либо конкретный запрос.
4. Вчера отлично поработал. Странно, но сегодня (на недавно установленном Pi) он не может разрешить половину временных меток. Ввод чисел в команду даты вручную работает. Но больше не в awk. Я понятия не имею, почему. дата всегда возвращает 0, что хорошо.
5. Можете ли вы предоставить некоторые примеры данных, о которых идет речь, для которых это решение не работает?
Ответ №2:
Если вы на самом деле просто хочу, чтобы дата время от $8
переформатировать вместо преобразования в секунды с начала эпохи С $9
К дата время, то вы можете просто сделать следующее, которое будет на порядки быстрее, чем вызов date
, поскольку это потребует awk
, чтобы породить подуровне один раз за входной строки для вызова date
из этого подуровня, которые будут крайне медленно.
Использование любого awk в любой оболочке на каждой коробке Unix:
$ cat tst.awk
BEGIN { FS=","; OFS="t" }
NR > 2 {
split($8,t," ")
mthNr = (index("JanFebMarAprMayJunJulAugSepOctNovDec",t[2]) 2)/3
print $2, $4, sprintf("d-d-d %s", t[5], mthNr, t[3], t[4])
}
$ awk -f tst.awk file
name1 10.0.0.1 2021-06-23 12:51:08
name2 10.0.0.2 2021-07-01 19:24:10
name3 10.0.0.3 2021-06-23 16:20:51
или, если вы действительно хотите использовать секунды эпохи от 9 долларов, используйте GNU awk для strftime()
, чтобы вам не нужно было создавать дочерние ячейки для вызова date
(но обратите внимание, что вывод теперь становится зависимым от TZ, как и в случае с date
):
$ cat tst.awk
BEGIN { FS=","; OFS="t" }
NR > 2 {
print $2, $4, strftime("%F %T",$9)
}
$ awk -f tst.awk file
name1 10.0.0.1 2021-06-23 07:51:08
name2 10.0.0.2 2021-07-01 14:24:10
name3 10.0.0.3 2021-06-23 11:20:51
$ TZ=UTC awk -f tst.awk file
name1 10.0.0.1 2021-06-23 12:51:08
name2 10.0.0.2 2021-07-01 19:24:10
name3 10.0.0.3 2021-06-23 16:20:51
или установите флаг UTC в strftime (), если UTC-это то, что у вас есть в ваших данных:
$ cat tst.awk
BEGIN { FS=","; OFS="t" }
NR > 2 {
print $2, $4, strftime("%F %T",$9,1)
}
$ awk -f tst.awk file
name1 10.0.0.1 2021-06-23 12:51:08
name2 10.0.0.2 2021-07-01 19:24:10
name3 10.0.0.3 2021-06-23 16:20:51
Ответ №3:
Возможно, это не самая эффективная awk
реализация, но после того, как я не смог понять, как работает решение @anubhava, я придумал этот хак.
awk -F"," '{ if (NR > 2 amp;amp; NR < 6){ print $2, $4, $8 }}' $file | sed -r 's/Mon|Tue|Wed|Thu|Fri|Sat|Sun//' | awk '{if ($4~/1/) $4="01"}{if ($3~/Jun/) $3="06" }{if ($3~/Jul/) $3="07"}{ print $1, $2, $6"-"$3"-"$4, $5}'
name1 10.0.0.1 2021-06-23 12:51:08
name2 10.0.0.2 2021-07-01 19:24:10
name3 10.0.0.3 2021-06-23 16:20:51