Пакетное переименование файлов с помощью Bash
-
03-07-2019 - |
Вопрос
Как Bash может переименовать серию пакетов, чтобы удалить их номера версий?Я забавлялся и с тем, и с другим expr
и %%
, но безрезультатно.
Примеры:
Xft2-2.1.13.pkg
становится Xft2.pkg
jasper-1.900.1.pkg
становится jasper.pkg
xorg-libXrandr-1.2.3.pkg
становится xorg-libXrandr.pkg
Решение
Вы могли бы использовать функцию расширения параметров bash
for i in ./*.pkg ; do mv "$i" "${i/-[0-9.]*.pkg/.pkg}" ; done
Имена файлов с пробелами заключаются в кавычки.
Другие советы
Если все файлы находятся в одном каталоге, последовательность
ls |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' |
sh
будешь делать свою работу.Тот Самый сэд команда создаст последовательность мв команды, которые затем вы можете передать в командную оболочку.Лучше всего сначала запустить конвейер без завершения | sh
чтобы убедиться, что команда выполняет то, что вы хотите.
Для рекурсии по нескольким каталогам используйте что-то вроде
find . -type f |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' |
sh
Обратите внимание , что в сэд последовательность группировки регулярных выражений представляет собой квадратные скобки, перед которыми стоит обратная косая черта, \(
и \)
, а не одиночные скобки (
и )
.
Я сделаю что-нибудь вроде этого:
for file in *.pkg ; do
mv $file $(echo $file | rev | cut -f2- -d- | rev).pkg
done
предполагается, что все ваши файлы находятся в текущем каталоге.Если нет, попробуйте использовать find, как советовал выше Хавьер.
Редактировать:Кроме того, эта версия не использует никаких специфичных для bash функций, как другие, описанные выше, что обеспечивает большую переносимость.
Вот POSIX, почти эквивалентный принятый в данный момент ответ.Это торгует только Bash ${variable/substring/replacement}
расширение параметров для того, которое доступно в любой оболочке, совместимой с Bourne.
for i in ./*.pkg; do
mv "$i" "${i%-[0-9.]*.pkg}.pkg"
done
Расширение параметра ${variable%pattern}
производит значение variable
с любым суффиксом, который соответствует pattern
Удалено.(Существует также ${variable#pattern}
чтобы удалить префикс.)
Я сохранил подшаблон -[0-9.]*
из общепринятого ответа, хотя это, возможно, вводит в заблуждение.Это не регулярное выражение, а глобальный шаблон;таким образом, это не означает "тире, за которым следует ноль или более цифр или точек".Вместо этого это означает "тире, за которым следует цифра или точка, за которой следует что угодно"."Что угодно" будет самым коротким из возможных совпадений, а не самым длинным.(Bash предлагает ##
и %%
для обрезки как можно более длинного префикса или суффикса, а не самого короткого.)
лучшее использование sed
для этого, что-то вроде:
find . -type f -name "*.pkg" |
sed -e 's/((.*)-[0-9.]*\.pkg)/\1 \2.pkg/g' |
while read nameA nameB; do
mv $nameA $nameB;
done
вычисление регулярного выражения оставлено в качестве упражнения (как и работа с именами файлов, содержащими пробелы).
Мы можем предположить sed
доступно на любом * nix, но мы не можем быть уверены
оно будет поддерживать sed -n
для генерации команд mv.(ПРИМЕЧАНИЕ: Только GNU sed
делает это.)
Тем не менее, используя встроенные функции bash и sed, мы можем быстро создать функцию оболочки для этого.
sedrename() {
if [ $# -gt 1 ]; then
sed_pattern=$1
shift
for file in $(ls $@); do
mv -v "$file" "$(sed $sed_pattern <<< $file)"
done
else
echo "usage: $0 sed_pattern files..."
fi
}
Использование
sedrename 's|\(.*\)\(-[0-9.]*\.pkg\)|\1\2|' *.pkg
before:
./Xft2-2.1.13.pkg
./jasper-1.900.1.pkg
./xorg-libXrandr-1.2.3.pkg
after:
./Xft2.pkg
./jasper.pkg
./xorg-libXrandr.pkg
Создание целевых папок:
С тех пор как mv
не создает автоматически целевые папки, которые мы не можем использовать
наша начальная версия sedrename
.
Это довольно небольшое изменение, поэтому было бы неплохо включить эту функцию:
Нам понадобится служебная функция, abspath
(или абсолютный путь), поскольку в bash
нет этой сборки.
abspath () { case "$1" in
/*)printf "%s\n" "$1";;
*)printf "%s\n" "$PWD/$1";;
esac; }
Как только у нас есть, что мы можем создать папку(ы) в течение СЭД/переименовать шаблон, который включает в себя новую структуру папок.
Это гарантирует, что мы знаем названия наших целевых папок.Когда мы переименовать нужно использовать его на имя целевого файла.
# generate the rename target
target="$(sed $sed_pattern <<< $file)"
# Use absolute path of the rename target to make target folder structure
mkdir -p "$(dirname $(abspath $target))"
# finally move the file to the target name/folders
mv -v "$file" "$target"
Вот полный сценарий с учетом папок...
sedrename() {
if [ $# -gt 1 ]; then
sed_pattern=$1
shift
for file in $(ls $@); do
target="$(sed $sed_pattern <<< $file)"
mkdir -p "$(dirname $(abspath $target))"
mv -v "$file" "$target"
done
else
echo "usage: $0 sed_pattern files..."
fi
}
Конечно, это все еще работает, когда у нас нет определенных целевых папок также.
Если бы мы хотели поместить все песни в папку, ./Beethoven/
мы можем это сделать:
Использование
sedrename 's|Beethoven - |Beethoven/|g' *.mp3
before:
./Beethoven - Fur Elise.mp3
./Beethoven - Moonlight Sonata.mp3
./Beethoven - Ode to Joy.mp3
./Beethoven - Rage Over the Lost Penny.mp3
after:
./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3
Бонусный раунд...
Использование этого скрипта для перемещения файлов из папок в одну папку:
Предполагая, что мы хотим собрать все соответствующие файлы и поместить их в текущую папку, мы можем это сделать:
sedrename 's|.*/||' **/*.mp3
before:
./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3
after:
./Beethoven/ # (now empty)
./Fur Elise.mp3
./Moonlight Sonata.mp3
./Ode to Joy.mp3
./Rage Over the Lost Penny.mp3
Примечание о шаблонах регулярных выражений sed
В этом скрипте применяются обычные правила шаблонов sed, эти шаблоны не являются
PCRE (регулярные выражения, совместимые с Perl).Вы могли бы использовать sed
расширенный синтаксис регулярных выражений, используя либо sed -r
или sed -E
в зависимости от вашей платформы.
Посмотрите, соответствует ли POSIX man re_format
полное описание
ООО Базовый и расширенный шаблоны регулярных выражений.
Я нахожу, что rename - гораздо более простой инструмент для использования в подобных ситуациях.Я нашел это на Homebrew для OSX
Для вашего примера я бы сделал:
rename 's/\d*?\.\d*?\.\d*?//' *.pkg
Буква "s" означает замену.Форма называется s/searchPattern/replacement/ files_to_apply.Для этого вам нужно использовать регулярное выражение, что требует небольшого изучения, но оно того стоит.
Кажется, это работает при условии, что
- все заканчивается на $ pkg
- ваша версия # всегда начинается с "-".
удалите файл .pkg, затем удалите файл -..
for x in $(ls); do echo $x $(echo $x | sed 's/\.pkg//g' | sed 's/-.*//g').pkg; done
У меня было несколько *.txt
файлы, которые будут переименованы как .sql
в той же папке.это сработало для меня:
for i in \`ls *.txt | awk -F "." '{print $1}'\` ;do mv $i.txt $i.sql; done
Спасибо вам за эти ответы.У меня тоже была какая-то проблема.Перемещение файлов .nzb., поставленных в очередь, в файлы .nzb.В именах файлов были пробелы и прочая ерунда, и это решило мою проблему:
find . -type f -name "*.nzb.queued" |
sed -ne "s/^\(\(.*\).nzb.queued\)$/mv -v \"\1\" \"\2.nzb\"/p" |
sh
Он основан на ответе Diomidis Spinellis.
Регулярное выражение создает одну группу для всего имени файла и одну группу для части перед .nzb.queued, а затем создает команду перемещения оболочки.Со строками, заключенными в кавычки.Это также позволяет избежать создания цикла в сценарии командной оболочки, потому что это уже сделано sed.