Как заменить временные метки эпох в файле на другие форматы?

short: обычно вы не можете этого сделать (если только выделение не было скопировано в буфер обмена)

long: есть несколько особых случаев, например, в xterm есть функция, которая (обычно отключенная) позволяет приложению читать выделенный текст через управляющую последовательность. Это описано в XTerm Control Sequences:

        Ps = 5 2  -> Manipulate Selection Data.  These controls may
      be disabled using the allowWindowOps resource.  The parameter
      Pt is parsed as
           Pc; Pd
      The first, Pc, may contain zero or more characters from the
      set c  p  s  0  1  2  3  4  5  6  7 .  It is used to construct
      a list of selection parameters for clipboard, primary, select,
      or cut buffers 0 through 7 respectively, in the order given.
      If the parameter is empty, xterm uses s 0 , to specify the
      configurable primary/clipboard selection and cut buffer 0.
      The second parameter, Pd, gives the selection data.  Normally
      this is a string encoded in base64.  The data becomes the new
      selection, which is then available for pasting by other appli-
      cations.
      If the second parameter is a ? , xterm replies to the host
      with the selection data encoded using the same protocol.
      If the second parameter is neither a base64 string nor ? ,
      then the selection is cleared.

То есть, если ресурс allowWindowOps включен, приложение может сделать что-то вроде

printf '\033]52;s;?\007'

и прочитать данные выделения как строку base64. Но это особый случай.

Некоторые приложения, конечно, копируют в буфер обмена (см. FAQ), но не все. Например, rxvt и т.д. используют первичное выделение. Не существует решения, которое работало бы везде.

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

10
18.11.2018, 12:05
6 ответов

Предполагая, что формат даты в вашем сообщении соответствует вашим требованиям, следующее регулярное выражение должно соответствовать вашим потребностям.

sed -E 's/\#(1[0-9]{9})(.*)/echo \1 $(date -d @\1)/e' log.file

Помните, что это заменит только одну эпоху в строке.

1
27.01.2020, 20:00

При условии согласованного формата файла с помощью bash вы можете читать файл построчно, проверять, соответствует ли он заданному формату, а затем выполните преобразование:

while IFS= read -r i; do [[ $i =~ ^#([0-9]{10})$ ]] && \
      date -d@"${BASH_REMATCH[1]}"; done <file.txt

BASH_REMATCH - это массив, первым элементом которого является первая захваченная группа в сопоставлении Regex, = ~ , в данном случае эпоха.


Если вы хотите сохранить файловую структуру:

while IFS= read -r i; do if [[ $i =~ ^#([0-9]{10})$ ]]; then printf '#%s\n' \
   "$(date -d@"${BASH_REMATCH[1]}")"; else printf '%s\n' "$i"; fi; done <file.txt

это выведет измененное содержимое в STDOUT, чтобы сохранить его в файле, например. out.txt :

while ...; do ...; done >out.txt

Теперь, если хотите, вы можете заменить исходный файл:

mv out.txt file.txt

Пример:

$ cat file.txt
#1472047795
ll /data/holding/email
#1472047906
cat /etc/rsyslog.conf
#1472048038
ll /data/holding/web

$ while IFS= read -r i; do [[ $i =~ ^#([0-9]{10})$ ]] && date -d@"${BASH_REMATCH[1]}"; done <file.txt
Wed Aug 24 20:09:55 BDT 2016
Wed Aug 24 20:11:46 BDT 2016
Wed Aug 24 20:13:58 BDT 2016

$ while IFS= read -r i; do if [[ $i =~ ^#([0-9]{10})$ ]]; then printf '#%s\n' "$(date -d@"${BASH_REMATCH[1]}")"; else printf '%s\n' "$i"; fi; done <file.txt
#Wed Aug 24 20:09:55 BDT 2016
ll /data/holding/email
#Wed Aug 24 20:11:46 BDT 2016
cat /etc/rsyslog.conf
#Wed Aug 24 20:13:58 BDT 2016
ll /data/holding/web
6
27.01.2020, 20:00

Все остальные ответы порождают новый процесс date для каждой даты эпохи, которую необходимо преобразовать. Это может потенциально увеличить накладные расходы на производительность, если ваш ввод большой.

Однако у GNU date есть удобная опция -f , которая позволяет одному экземпляру процесса date непрерывно читать входные даты без необходимости в новой вилке. Таким образом, мы можем использовать sed , paste и date таким образом, чтобы каждый из них создавался только один раз (2x для sed ) независимо от размера ввода:

$ paste -d '\n' <( sed '2~2d;y/#/@/' epoch.txt | date -f - ) <( sed '1~2d' epoch.txt )
Wed Aug 24 07:09:55 PDT 2016
ll /data/holding/email
Wed Aug 24 07:11:46 PDT 2016
cat /etc/rsyslog.conf
Wed Aug 24 07:13:58 PDT 2016
ll /data/holding/web
$ 
  • Две команды sed соответственно в основном удаляют четные и нечетные строки ввода; первый также заменяет # на @ , чтобы указать правильный формат временной метки эпохи.
  • Первый вывод sed затем передается по конвейеру date -f , который выполняет необходимое преобразование даты для каждой строки ввода, которую он получает.
  • Затем эти два потока чередуются в единый требуемый вывод с использованием пасты . Конструкции <() представляют собой подстановки процесса bash , которые эффективно обманывают вставку, заставляя думать, что она считывает данные из заданных имен файлов, тогда как на самом деле она считывает выходные данные, переданные из команды внутри. -d '\ n' указывает paste разделять четные и нечетные выходные строки символом новой строки. Вы можете изменить (или удалить) это, если, например, вы хотите, чтобы метка времени находилась в той же строке, что и другой текст.

Обратите внимание, что в этой команде есть несколько GNUisms и Bashisms. Это несовместимо с Posix, и не следует ожидать, что его можно будет переносить за пределы мира GNU / Linux. Например, date -f делает что-то еще в варианте OSX BSD date .

4
27.01.2020, 20:00

Хотя это возможно с GNU sed с такими вещами, как:

sed -E 's/^#([0-9]+).*$/date -d @\1/e'

Это было бы ужасно неэффективно (и легко ввести произвольную команду инъекции уязвимостей 1 ), поскольку это означало бы запуск одной оболочки и одной команды date для каждой #xxxx строки, практически как плохо, как оболочка ] при чтении цикла . Здесь было бы лучше использовать такие вещи, как perl или gawk , то есть утилиты обработки текста со встроенными возможностями преобразования даты:

perl  -MPOSIX -pe 's/^#(\d+).*/ctime $1/se'

Или:

gawk '/^#/{$0 = strftime("%c", substr($0, 2))};1'

1 Если бы мы написали ^ # ([0-9]). * вместо ^ # ([0-9]). * $ (как я делал ранее версия этого ответа), затем в многобайтовых локали, таких как UTF-8 (норма в настоящее время), с вводом типа # 1472047795 <0x80>; reboot , где это <0x80> - это байтовое значение 0x80, которое не является допустимым символом, поэтому команда s завершилась бы выполнением date -d @ 1472047795 <0x80>; перезагрузите, например, . В то время как с дополнительным $ , эти строки не будут заменены.Альтернативный подход: s / ^ # ([0-9]) / date -d @ \ 1 # / e , то есть оставить часть после даты #xxx . как комментарий оболочки

14
27.01.2020, 20:00

с использованием sed:

sed -r 's/\#([0-9]*)/echo $(date -d @\1)/eg' test.txt

вывод:

ر أغس 24 16:09:55 EET 2016
ll /data/holding/email
ر أغس 24 16:11:46 EET 2016
cat /etc/rsyslog.conf
ر أغس 24 16:13:58 EET 2016
ll /data/holding/web

, поскольку мой язык локали - арабский :)

0
27.01.2020, 20:00

Мое решение, как сделать это в конвейере

cat test.txt | sed 's/^/echo "/; s/\([0-9]\{10\}\)/`date -d @\1`/; s/$/"/' | bash
0
27.01.2020, 20:00

Теги

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