Пакетное переименование файлов с помощью Bash - PullRequest
90 голосов
/ 02 марта 2009

Как 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

Ответы [ 10 ]

173 голосов
/ 02 марта 2009

Вы можете использовать функцию расширения параметров bash

for i in ./*.pkg ; do mv "$i" "${i/-[0-9.]*.pkg/.pkg}" ; done

Кавычки необходимы для имен файлов с пробелами.

32 голосов
/ 02 марта 2009

Если все файлы находятся в одном каталоге, последовательность

ls | 
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' | 
sh

сделает вашу работу. Команда sed создаст последовательность команд mv , которые затем можно направить в оболочку. Лучше всего сначала запустить конвейер без завершающего | sh, чтобы убедиться, что команда делает то, что вам нужно.

Чтобы пройти через несколько каталогов, используйте что-то вроде

find . -type f |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' |
sh

Обратите внимание, что в sed последовательность группировки регулярных выражений - это скобки, перед которыми стоит обратный слеш, \( и \), а не одиночные скобки ( и ).

10 голосов
/ 02 марта 2009

Я сделаю что-то вроде этого:

for file in *.pkg ; do
    mv $file $(echo $file | rev | cut -f2- -d- | rev).pkg
done

предполагается, что все ваши файлы находятся в текущем каталоге. Если нет, попробуйте использовать find, как указано выше Хавьером.

РЕДАКТИРОВАТЬ : Кроме того, эта версия не использует никаких специфических для bash функций, как другие выше, что приводит к большей мобильности.

4 голосов
/ 04 марта 2018

Здесь POSIX почти эквивалентен принятому в настоящее время ответу . Это меняет расширение параметра Bash-only ${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 предлагает ## и %% для усечения самого длинного возможного префикса или суффикса, а не самого короткого.)

3 голосов
/ 22 августа 2018

Я считаю, что переименование является гораздо более простым инструментом для такого рода вещей. Я нашел его на Homebrew для OSX

Для вашего примера я бы сделал:

rename 's/\d*?\.\d*?\.\d*?//' *.pkg

's' означает замену. Форма s / searchPattern / replace / files_to_apply. Вам нужно использовать регулярные выражения для этого, которое требует небольшого изучения, но оно того стоит.

3 голосов
/ 11 марта 2018

Мы можем предположить, что sed доступен для любого * nix, но мы не можем быть уверены, он будет поддерживать sed -n для генерации mv-команд. ( ПРИМЕЧАНИЕ: Только GNU sed делает это.)

Несмотря на это, bash buildins и 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; }

Как только мы получим, мы можем сгенерировать целевую папку (и) для Шаблон sed / rename, включающий новую структуру папок.

Это гарантирует, что мы знаем имена наших целевых папок. Когда мы переименуйте нам нужно использовать его в имени целевого файла.

# 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 regex

В этом сценарии применяются обычные правила шаблонов sed, эти шаблоны не PCRE (регулярные выражения, совместимые с Perl). Вы могли бы сед расширенный синтаксис регулярного выражения с использованием sed -r или sed -E в зависимости от вашей платформы.

См. POSIX-совместимое man re_format для полного описания Sed базовые и расширенные шаблоны регулярных выражений.

3 голосов
/ 02 марта 2009

лучше использовать 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

вычисление регулярного выражения оставлено в качестве упражнения (как в случае с именами файлов, которые содержат пробелы)

1 голос
/ 05 ноября 2015

У меня было несколько *.txt файлов для переименования в .sql в одной папке. ниже работал для меня:

for i in \`ls *.txt | awk -F "." '{print $1}'\` ;do mv $i.txt $i.sql; done
1 голос
/ 02 марта 2009

Кажется, это работает, если предположить, что

  • все заканчивается $ pkg
  • Ваша версия # всегда начинается с "-"

снимите .pkg, затем снимите - ..

for x in $(ls); do echo $x $(echo $x | sed 's/\.pkg//g' | sed 's/-.*//g').pkg; done
0 голосов
/ 30 октября 2014

Спасибо за ответы. У меня тоже была какая-то проблема. Перемещение файлов .nzb.queued в файлы .nzb. В именах файлов были пробелы и другие символы, и это решило мою проблему:

find . -type f -name "*.nzb.queued" |
sed -ne "s/^\(\(.*\).nzb.queued\)$/mv -v \"\1\" \"\2.nzb\"/p" |
sh

Он основан на ответе Диомидиса Спинеллиса.

Регулярное выражение создает одну группу для всего имени файла и одну группу для части перед .nzb.queued, а затем создает команду перемещения оболочки. С указанными строками. Это также позволяет избежать создания цикла в сценарии оболочки, поскольку это уже сделано с помощью sed.

...