Добавление метки времени первой строки к списку ниже.

Одним из основных нововведений daemontools в середине 1990-х годов была идея надежного логирования . Демон-супервизор управлял двумя демонами, главным демоном и логарифмическим демоном. Он открыл канал перед вызовом любого из них, передав конец канала для записи в качестве стандартного вывода основного процесса демона, а конец канала для чтения в качестве стандартного ввода log процесса демона.. Он также сам хранил дескрипторы файлов открытыми на обоих концах канала. Таким образом, если демон журнала умер или был остановлен, а затем перезапущен, данные журнала, записанные в канал, будут сохранены и доступны для чтения и регистрации только что перезапущенным экземпляром демона журнала, потому что канал не будет закрыт.

Разработчики systemd не извлекли уроков из этого проекта.

systemdорганизует порожденные сервисные процессы, если они настроены, чтобы их стандартные выходные данные отправлялись через сокет в процесс журнала. Но вместо того, чтобы, как в случае с daemontools, открывать канал и организовывать для каждого процесса наследование соответствующего конца канала, systemdиспользует потоковый сокет AF_LOCALпо адресу /run/systemd/journal/stdout. Основные процессы подключаются как клиенты; процесс журнала слушает как сервер.

Это означает, что соединение данных имеет семантику клиентского -серверного сокета, а не унаследованную семантику канала. Если сервер умирает, соединение обрывается. Все буферизованные данные журнала теряются, а все дальнейшие данные, записанные основным демоном, отправляются в закрытый сокет и теряются. (Отчасти поэтому systemd имеет настройку IgnoreSIGPIPE, которая по умолчанию имеет значение true. Запись вывода журнала в закрытый сокет также приводит к тому, что ядро ​​пытается убить демона, записывающего журнал.)

Таким образом, если вы завершите процесс systemd-journald, процесс выхода закроет соединения сокетов со всеми демоническими процессами, выходные данные которых он регистрирует, и весь дальнейший их вывод будет потерян.Невозможно исправить это и повторно подключить выходные данные основного процесса к процессу журнала. Необходимо перезапустить все основные процессы, чтобы systemdre -открыло новое клиентское соединение с (недавно созданным )сервером журналов.

В 2016 году разработчики systemd решили обойти эту проблему. Это включало добавление процессам возможности передавать произвольные дескрипторы открытых файлов по своему выбору в процесс #1 и извлекать их позже. systemd-journaldделает это, когда сервер -завершает соединения с клиентами, которые он регистрирует, так что они остаются открытыми после перезапуска самого демона регистрации.

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

В s6 Лорана Берко s6-svscanделает обычную вещь daemontools с каналом между main и log сервисами. Он также имеет механизм для сохранения открытыми произвольных файловых дескрипторов. Это не делается кодом, работающим с привилегиями суперпользователя внутри диспетчера служб в процессе #1, как в systemd. Это делается совершенно непривилегированным процессом, который запускается отдельно от диспетчера служб.

Дальнейшее чтение

1
10.10.2019, 23:27
2 ответа
awk -F'[/: ]' '{
  if (NF==6){
    ts=mktime($3" "$1" "$2" "$4" "$5" "$6)
    skipheader=1
  }
  else if (NF==0 || skipheader){
    skipheader=0
  }
  else {
    print "name="$1,"cans="$2,"bucks="$3,"puns="$4,"ts="ts
  }
}' file
  • Разделите поля на /, :и символ пробела, чтобы получить отдельные части даты и времени.
  • Если количество полей равно 6, создайте метку времени tsи установите флаг для пропуска следующей строки заголовка.
  • Если количество полей равно нулю или установлен флаг skipheader, сбросить флаг skipheader.
  • В противном случае распечатайте данные.

Выход:

name=Clyde cans=12 bucks=2 puns=79 ts=1566468058
name=Sheila cans=32 bucks=16 puns=42 ts=1566468058
name=Elmo cans=44 bucks=18 puns=21 ts=1566468058
name=Clyde cans=18 bucks=21 puns=46 ts=1566579622
name=Sheila cans=37 bucks=2 puns=11 ts=1566579622
name=Elmo cans=41 bucks=3 puns=10 ts=1566579622
2
27.01.2020, 23:30

Следующий perl-скрипт будет работать с любым количеством полей и любыми именами полей во входных данных.

Требуются библиотечные модули Time ::Date и List ::MoreUtils . Оба они, вероятно, уже упакованы для вашего дистрибутива (, например. на дебианеsudo apt-get install libtimedate-perl liblist-moreutils-perl). Сценарий можно было бы написать так, что эти модули ему не нужны, но нет смысла заново -изобретать колесо, когда уже существует повторно -пригодный для использования библиотечный код, который выполняет именно ту работу, которая вам нужна.

Сценарий предполагает, что строки ввода ограничены любым количеством пробелов (, т. е. одним или несколькими пробелами, символами табуляции и т. д. ). Если ввод разделен табуляцией -, измените строки с split;на split /\t/;. Разделители табуляции были бы хорошей идеей, если бы любое из имен в первом поле содержало пробелы (, например. Firstname Surname), или если какое-либо из имен полей содержало пробелы.

Если вам нужно жестко -закодировать определенный часовой пояс, например. GMT, измените следующую строку:

    $ts = str2time($_);

к, например,. (обратите внимание на пробел в кавычках передGMT):

    $ts = str2time($_. ' GMT');
#!/usr/bin/perl

use strict;
use Date::Parse;
use List::MoreUtils qw(pairwise);

my @columns;
my $ts='';

while(<>) {
  s/^\s*|\s*$//g;    #/ strip leading and trailing spaces
  next if (/^$/);    #/ skip empty lines
  chomp;

  # line begins with two digits and a slash? it's a date.
  if (m/^\d\d\//) {
    # get the date and parse it so that we have seconds since the epoch
    $ts = str2time($_);

    # get the next line and split it into column headers
    $_ = readline;
    @columns = split;

  } else {
    # split the current line into @row array
    my @row=split;
    # use pairwise() function from List::MoreUtils module to merge the
    # @columns and @row arrays.
    print join(" ", (pairwise { "$a=$b" } @columns, @row), "ts=$ts"), "\n";
  }

}

Пример вывода:

$./reformat.pl input.txt 
Name=Clyde Cans=12 Bucks=2 Puns=79 ts=1566439258
Name=Sheila Cans=32 Bucks=16 Puns=42 ts=1566439258
Name=Elmo Cans=44 Bucks=18 Puns=21 ts=1566439258
Name=Clyde Cans=18 Bucks=21 Puns=46 ts=1566550822
Name=Sheila Cans=37 Bucks=2 Puns=11 ts=1566550822
Name=Elmo Cans=41 Bucks=3 Puns=10 ts=1566550822

ПРИМЕЧАНИЕ. :Это не сломается, если в какой-либо из строк данных будет больше или меньше столбцов, чем количество заголовков столбцов, но это приведет к необычному выводу. Для отсутствующих полей он просто напечатает имена полей с символом =и без значения (, например. если в строке ввода было только 2 поля, она вывела бы Puns=), а если бы полей было больше, она бы напечатала только значение с префиксом =(, например.если в строке ввода есть дополнительное поле со значением 20, будет выведено =20).

напр. если в вашем образце входных данных был третий блок данных, подобный этому:

08/23/2019 23:30:01
Name Cans Bucks Puns
Clyde 18 21 46
Sheila 37 2 11
Elmo 41 3 10
Missing 41 3
Extra 41 3 10 20

Это приведет к дополнительному выводу:

Name=Clyde Cans=18 Bucks=21 Puns=46 ts=1566567001
Name=Sheila Cans=37 Bucks=2 Puns=11 ts=1566567001
Name=Elmo Cans=41 Bucks=3 Puns=10 ts=1566567001
Name=Missing Cans=41 Bucks=3 Puns= ts=1566567001
Name=Extra Cans=41 Bucks=3 Puns=10 =20 ts=1566567001
0
27.01.2020, 23:30

Теги

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