#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
о том, чтобы поместить код из этого ответа между thedo
и thedone
.3. Обратите внимание на изменение с
%sn
на%s
— вы не можете безопасно хранить список произвольных имен файлов в списке, разделенном новой строкой, потому что для имен файлов законно содержать новые строки как часть их текста; символ NUL — единственный, который гарантированно не присутствует в имени файла.4. Если это другая проблема, с которой вы столкнулись сейчас, задайте новый вопрос.
5. Спасибо за этот совет @Charles Duffy. Как вы можете видеть, я все еще учусь, как это сделать. Единственная проблема заключается в том, что опубликованный вами код основан на том, что имена файлов находятся в формате
The New Town Cryer - 01 Oct 2020.pdf
, поскольку это формат, который был бы введен в строку в моем первоначальном вопросе. Однако это имя файла является результатом первых 4sed
команд в моем скрипте. Исходные имена файлов представлены в формате: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 согласен — пользователь остерегается