Измените параметр awk с помощью другой команды

#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