Пакетное переименование файлов с помощью Bash

StackOverflow https://stackoverflow.com/questions/602706

  •  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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top