Используйте команду date для переформатирования даты в команде sed в скрипте bash

#linux #bash #date #sed

#linux #bash #Дата #sed

Вопрос:

Я пытаюсь переименовать серию pdf-файлов из таких имен файлов: The New Town Cryer - 01 Oct 2020.pdf в this 2020-10-01_-_The_New_Town_Cryer.pdf . Я написал скрипт bash, который использует sed для этого, но у меня возникли проблемы с пониманием того, как преобразовать дату из текущего трехбуквенного формата месяца с помощью date команды. На данный момент это строка моего скрипта (предыдущая newname переменная The New Town Cryer - 01 Oct 2020 pdf :

 newname="$(echo "$newname" | sed -re 's/^(.*) - (.*) ([^ ] )$/echo "$(date -d "2" " %Y-%m-%d")-1".3/')"
  

Вывод из этой строки echo "$(date -d"01 Oct 2020" " %Y-%m-%d")-The New Town Cryer".pdf , где я надеялся, что это будет 2020-10-01-The New Town Cryer.pdf

Кто-нибудь может сказать мне, где я ошибаюсь? Спасибо!

Редактировать: чтобы уточнить, вот пока весь мой сценарий, поскольку кажется, что мой фрагмент был неясным. Исходный формат имен The New Town Cryer - No. 1,032 [01 Oct 2020].pdf файлов, который я пытаюсь преобразовать в формат 2020-10-01_The_New_Town_Cryer.pdf .

 #!/bin/bash

find "$1" "*.pdf" -type f -printf "%fn" | while IFS= read -r f ; do #find all pdfs
  name=$f
  newname="$(echo "$name" | sed -re 's/./ /g')" # replace .s with spaces to allow 'date'-command to parse the date
  newname="$(echo "$newname" | sed -re 's/[/!/g')" # replace [s with spaces to allow 'date'-command to parse the date
  newname="$(echo "$newname" | sed -re 's/]/!/g')" # replace [s with spaces to allow 'date'-command to parse the date
  newname="$(echo "$newname" | sed -re 's/(.*) - (.*) (!.*!)/1 - 3/')" # remove issue number
  newname="$(echo "$newname" | sed -re 's/!//g')" # replace !s with spaces to allow 'date'-command to parse the date
  newname="$(echo "$newname" | sed -re 's/^(.*) - (.*) ([^ ] )$/echo "$(date -d "2" " %Y-%m-%d")-1".3/')" # reorder the date and name, split at '-', keep the file extension, prepare for date conversion
  newname="$(echo "$newname" | bash )"
  newname="$(echo "$newname" | sed -re 's/ /./g')" # replace remaining spaces with .
  mv "$name" "$newname"
done
  

Комментарии:

1. Мне очень трудно читать этот код. Почему у вас есть echo команда в правой части вашего sed выражения replace?

2.(и как на самом деле будет выполняться sed вывод чего-либо в виде команды, чтобы вызвать это эхо?)

3. Пожалуйста, посмотрите полный сценарий, который я добавил к исходному вопросу.

4. Код, добавленный к вопросу, крайне неэффективен, а также имеет серьезные ошибки безопасности. Замены команд выполняются медленно. Вызовы внешних команд выполняются медленно. bash Очень сложно выполнить безопасную передачу сгенерированного кода. Не делайте ничего из этого.

5. Хорошо, спасибо, что дали мне знать. Я все еще учусь, поэтому, очевидно, допускаю много ошибок. Я обновлю свой код в соответствии с вашими предложениями ниже. Еще раз спасибо.

Ответ №1:

Использование встроенной поддержки регулярных выражений в bash вместо попыток использования (ab) sed здесь делает код — хотя, возможно, и длиннее — намного понятнее для чтения. В качестве решения вы можете увидеть работу в https://ideone.com/Suw9Ow:

 oldname='The New Town Cryer - 01 Oct 2020.pdf'
date_re='(^.*) - ([[:digit:]]{2}) ([[:alpha:]] ) ([[:digit:]]{4})(.*)'
if [[ $oldname =~ $date_re ]]; then
  basename=${BASH_REMATCH[1]}
  day=${BASH_REMATCH[2]}
  month=${BASH_REMATCH[3]}
  year=${BASH_REMATCH[4]}
  ext=${BASH_REMATCH[5]}
  new_date=$(date -d "${day} ${month} ${year}"  %Y-%m-%d)
  newname="${new_date} - ${basename}${ext}"
  echo "Old name: $oldname"
  echo "New name: $newname"
fi
  

Комментарии:

1. Хорошо, похоже, я смогу полностью обновить свой скрипт, чтобы переименовать файлы, используя поддержку регулярных выражений этого bash. Будет ли это работать с find командой, подобной той, что в скрипте, который я добавил выше?

2. Подумайте find "$1" -name "*.pdf" -type f -printf '%f' | while IFS= read -r -d '' oldname; do ...; done о том, чтобы поместить код из этого ответа между the do и the done .

3. Обратите внимание на изменение с %sn на %s — вы не можете безопасно хранить список произвольных имен файлов в списке, разделенном новой строкой, потому что для имен файлов законно содержать новые строки как часть их текста; символ NUL — единственный, который гарантированно не присутствует в имени файла.

4. Если это другая проблема, с которой вы столкнулись сейчас, задайте новый вопрос.

5. Спасибо за этот совет @Charles Duffy. Как вы можете видеть, я все еще учусь, как это сделать. Единственная проблема заключается в том, что опубликованный вами код основан на том, что имена файлов находятся в формате The New Town Cryer - 01 Oct 2020.pdf , поскольку это формат, который был бы введен в строку в моем первоначальном вопросе. Однако это имя файла является результатом первых 4 sed команд в моем скрипте. Исходные имена файлов представлены в формате: The New Town Cryer - No. 1,032 [01 Oct 2020].pdf . Из этого я хочу удалить номер проблемы и переименовать в этот формат : 2020-10-01_The_New_Town_Cryer.pdf .

Ответ №2:

Это может сработать для вас (GNU sed):

 sed -E 's/(.*) - (.*).(.*)/echo $(date -d "2" " %Y-%m-%d")-1.3/e' file
  

Сопоставьте имя файла, а затем используйте e флаг для оценки команды echo.

Комментарии:

1. Я действительно не рекомендую никому использовать флаг e sed, особенно когда команда содержит ссылки, которые расширяются до .* содержимого a; это eval эквивалентно, поэтому легко обнаружить ошибки безопасности, когда значение неожиданно содержит что-то, что действует как синтаксис оболочки.

2. …если у вас был входной файл, созданный командой touch $'Hello - $(rm -rf ~).pdf' , вы не хотите date -d "$(rm -rf ~)" " %Y-%m-%d" , чтобы его запускали. И хотя это вредоносный пример, могут произойти и менее преднамеренные.

3. @CharlesDuffy согласен — пользователь остерегается