Переименовать несколько файлов в Unix

В каталоге несколько файлов, начинающихся с префикса fgh, например:

fghfilea
fghfileb
fghfilec

Я хочу переименовать их все, чтобы начать с префикса jkl, Есть ли одна команда, чтобы сделать это вместо переименования каждого файла в отдельности?

26 ответов

Есть несколько способов, но с помощью rename вероятно будет самым легким.

Используя одну версию rename:

rename 's/^fgh/jkl/' fgh*

Используя другую версию rename (так же, как ответ Judy2K):

rename fgh jkl fgh*

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

Вот как sed а также mv можно использовать вместе для переименования:

for f in fgh*; do mv "$f" $(echo "$f" | sed 's/^fgh/jkl/g'); done

Согласно комментарию ниже, если в именах файлов есть пробелы, кавычки, возможно, должны окружать подфункцию, которая возвращает имя для перемещения файлов:

for f in fgh*; do mv "$f" "$(echo $f | sed 's/^fgh/jkl/g')"; done

Переименование может быть не в каждой системе. так что если у вас его нет, используйте оболочку этого примера в bash shell

for f in fgh*; do mv "$f" "${f/fgh/xxx}";done

Используя mmv:

mmv "fgh*" "jkl#1"

Есть много способов сделать это (не все из них будут работать на всех системах Unixy):

  • ls | cut -c4- | xargs -I§ mv fgh§ jkl§

    § можно заменить на что угодно. Вы могли бы сделать это с find -exec тоже, но это ведет себя немного по-разному во многих системах, поэтому я обычно избегаю

  • for f in fgh*; do mv "$f" "${f/fgh/jkl}";done

    Грубый но эффективный как говорится

  • rename 's/^fgh/jkl/' fgh*

    Очень мило, но переименования нет в BSD, которая является самой распространенной системой Unix на данный момент.

  • rename fgh jkl fgh*

  • ls | perl -ne 'chomp; next unless -e; $o = $_; s/fgh/jkl/; next if -e; rename $o, $_';

    Если вы настаиваете на использовании Perl, но в вашей системе нет переименования, вы можете использовать этого монстра.

Некоторые из них немного запутаны, и этот список далеко не полон, но вы найдете то, что вам нужно здесь, практически для всех Unix-систем.

rename fgh jkl fgh*

С помощью find, xargs а также sed:

find . -name "fgh*" -type f -print0 | xargs -0 -I {} sh -c 'mv "{}" "$(dirname "{}")/`echo $(basename "{}") | sed 's/^fgh/jkl/g'`"'

Это сложнее, чем решение @nik, но позволяет рекурсивно переименовывать файлы. Например, структура,

.
├── fghdir
│   ├── fdhfilea
│   └── fghfilea
├── fghfile\ e
├── fghfilea
├── fghfileb
├── fghfilec
└── other
    ├── fghfile\ e
    ├── fghfilea
    ├── fghfileb
    └── fghfilec

будет преобразован в это,

.
├── fghdir
│   ├── fdhfilea
│   └── jklfilea
├── jklfile\ e
├── jklfilea
├── jklfileb
├── jklfilec
└── other
    ├── jklfile\ e
    ├── jklfilea
    ├── jklfileb
    └── jklfilec

Ключ, чтобы заставить это работать с xargs это вызвать оболочку из xargs.

Общая команда будет

find /path/to/files -name '<search>*' -exec bash -c 'mv $0 ${0/<search>/<replace>}' {} \;

куда <search>а также <replace>должны быть заменены вашим источником и целью соответственно.

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

find . -name 'gfh*' -exec bash -c 'mv $0 ${0/gfh/jkl}' {} \;

Для "пробного прогона" добавить echoдо mv, чтобы вы видели, какие команды генерируются:

find . -name 'gfh*' -exec bash -c 'echo mv $0 ${0/gfh/jkl}' {} \;

Чтобы установить скрипт переименования Perl:

sudo cpan install File::Rename

Есть два переименования, как указано в комментариях в ответе Stephan202. Дистрибутивы на основе Debian переименованы в Perl. Redhat/rpm дистрибутивы имеют C переименование.
OS X не имеет установленного по умолчанию (по крайней мере, в 10.8), равно как и Windows/Cygwin.

Вот способ сделать это с помощью командной строки Groovy:

groovy -e 'new File(".").eachFileMatch(~/fgh.*/) {it.renameTo(it.name.replaceFirst("fgh", "jkl"))}'

На Солярисе вы можете попробовать:

for file in `find ./ -name "*TextForRename*"`; do 
    mv -f "$file" "${file/TextForRename/NewText}"
done

Этот сценарий работал у меня для рекурсивного переименования с именами каталогов / файлов, возможно, содержащих пробелы:

find . -type f -name "*\;*" | while read fname; do
    dirname=`dirname "$fname"`
    filename=`basename "$fname"`
    newname=`echo "$filename" | sed -e "s/;/ /g"`
    mv "${dirname}/$filename" "${dirname}/$newname"
done

Обратите внимание на sed выражение, которое в этом примере заменяет все вхождения ; с пространством . Это, конечно, должно быть заменено в соответствии с конкретными потребностями.

#!/bin/sh

#replace all files ended witn .f77 to .f90 in a directory

for filename in *.f77
do 
    #echo $filename
    #b= echo $filename | cut -d. -f1
    #echo $b    
    mv "${filename}" "${filename%.f77}.f90"    
done

Я бы порекомендовал использовать свой собственный скрипт, который решает эту проблему. У него также есть опции для изменения кодировки имен файлов и для преобразования комбинированных диакритических знаков в заранее составленные символы, проблема, с которой я всегда сталкиваюсь, когда копирую файлы с моего Mac.

https://github.com/kugland/rename.pl/blob/master/rename.pl

Использование инструментов StringSolver (Windows & Linux Bash), которые обрабатываются на примерах:

filter fghfilea ok fghreport ok notfghfile notok; mv --all --filter fghfilea jklfilea

Сначала он вычисляет фильтр на основе примеров, где входными данными являются имена файлов и выходные данные (ok и notok, произвольные строки). Если бы фильтр имел опцию --auto или был вызван один после этой команды, он создал бы папку ok и папка notok и нажмите файлы соответственно им.

Затем с помощью фильтра mvКоманда - это полуавтоматическое движение, которое становится автоматическим с модификатором --auto. Используя предыдущий фильтр благодаря --filter, он находит отображение из fghfilea в jklfilea а затем применяет его ко всем отфильтрованным файлам.


Другие однолинейные решения

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

filter fghfilea ok fghreport ok notfghfile notok; mv --filter fghfilea jklfilea; mv
filter fghfilea ok fghreport ok notfghfile notok; auto --all --filter fghfilea "mv fghfilea jklfilea"
# Even better, automatically infers the file name
filter fghfilea ok fghreport ok notfghfile notok; auto --all --filter "mv fghfilea jklfilea"

Многошаговое решение

Чтобы тщательно определить, хорошо ли работают команды, вы можете набрать следующее:

filter fghfilea ok
filter fghfileb ok
filter fghfileb notok

и когда вы уверены, что фильтр хорош, выполните первый шаг:

mv fghfilea jklfilea

Если вы хотите проверить и использовать предыдущий фильтр, введите:

mv --test --filter

Если преобразование не то, что вы хотели (например, даже с mv --explain Вы видите, что что-то не так), вы можете ввести mv --clear перезапустить движущиеся файлы или добавить больше примеров mv input1 input2 где input1 и input2 - другие примеры

Когда вы уверены, просто введите

mv --filter

и вуаля! Все переименование осуществляется с помощью фильтра.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я являюсь соавтором этой работы, созданной для академических целей. Возможно, скоро появится функция создания bash.

Используя renamer:

$ renamer --find /^fgh/ --replace jkl * --dry-run

Удалить --dry-run Отметьте, когда вы довольны, результат выглядит правильно.

Моя версия переименования массовых файлов:

for i in *; do
    echo "mv $i $i"
done |
sed -e "s#from_pattern#to_pattern#g” > result1.sh
sh result1.sh

Еще один возможный параметр расширения:

for f in fgh*; do mv -- "$f" "jkl${f:3}"; done

Это было намного проще (на моем Mac) сделать это в Ruby. Вот 2 примера:

# for your fgh example. renames all files from "fgh..." to "jkl..."
files = Dir['fgh*']

files.each do |f|
  f2 = f.gsub('fgh', 'jkl')
  system("mv #{f} #{f2}")
end

# renames all files in directory from "021roman.rb" to "021_roman.rb"
files = Dir['*rb'].select {|f| f =~ /^[0-9]{3}[a-zA-Z]+/}

files.each do |f|
  f1 = f.clone
  f2 = f.insert(3, '_')
  system("mv #{f1} #{f2}")
end

Это расширенная версия решения +++.

Оригинальные решения: это и это .

Требования: поиск, обрезка, регулярное выражение, переименование

  1. Я хочу переименовать несколько файлов во многих папках.
  2. Некоторые папки должны быть удалены/исключены.
  3. я наcygwinи не могу получитьperl renameдля работы, что требуется для самого популярного решения (и я полагаю, что оно медленное, поскольку, похоже, у него нет возможности обрезки?)

Решение

  1. Использоватьfindдля эффективного получения файлов (с обрезкой) и с множеством параметров настройки.
  2. Используйте для замены регулярного выражения.
  3. Использоватьxargsчтобы направить результат в окончательную команду.

Пример 1: переименовать*.jsфайлы, но игнорировать

В этом примере выполняется поиск файлов и отображение найденного файла и переименованного файла. Из соображений безопасности он пока ничего не двигает. Вы должны заменить на для этого.

      set -x # stop on error
set -e # verbose mode (echo all commands)

find "." -type f -not \( -path "**/node_modules/**" -prune \) -name "*.js" | 
  sed -nE "s/(.*)\/my(.*)/& \1\/YOUR\2/p" |
  xargs -n 2 echo  # echo first (replace with `mv` later)

Приведенный выше скрипт превращает это:

      ./x/y/my-abc.js

В это:

      ./x/y/YOUR-abc.js

Разбивка решения

  1. find "." -type f -not \( -path "**/node_modules/**" -prune \) -name "*.js"
    • Ищет файлы (-type f).
    • The -notчасть исключает (и, что важно, не пересекает!)node_modulesпапка.
    • Имя файла должно совпадать"*.js".
    • Вы можете добавить дополнительные пункты включения и исключения.
    • Ссылки:
  2. sed -nE "s/(.*)\/my\-(.*\.js)/& \1\/YOUR-\2/p"
    • ПРИМЕЧАНИЕ: всегда нужно привыкнуть.
    • -Eвключает "расширенный" (т.е. более современный) синтаксис регулярных выражений.
    • используется в сочетании с завершающим флагом:-nскрывает все результаты, а/pнапечатает только совпадающие результаты. Таким образом, мы видим/перемещаем только те файлы, которые необходимо изменить, и игнорируем все остальные.
    • Замена регулярного выражения наsed(и другие инструменты регулярных выражений) всегда имеет формат:s/regex/replacement/FLAGS
    • Взамен,&представляет совпадающую входную строку. Это будет первым аргументомmv.
    • Ссылки:
  3. xargs -n 2 echo
    • Запустите командуechoс (первыми двумя строками) замененной строки.
    • Ссылки: человек xargs

Удачи!

Общий скрипт для запуска sed Выражение в списке файлов (объединяет sed Решение с rename решение):

#!/bin/sh

e=$1
shift

for f in $*; do
    fNew=$(echo "$f" | sed "$e")
    mv "$f" "$fNew";
done

Вызвать, передав скрипт sed выражение, а затем любой список файлов, так же, как версия rename:

script.sh 's/^fgh/jkl/' fgh*

Я написал этот скрипт для поиска всех файлов.mkv, рекурсивно переименовывая найденные файлы в.avi. Вы можете настроить его под свои нужды. Я добавил некоторые другие вещи, такие как получение файлового каталога, расширения, имени файла из пути к файлу, только в том случае, если вам понадобится обратиться к чему-то в будущем.

find . -type f -name "*.mkv" | while read fp; do 
fd=$(dirname "${fp}");
fn=$(basename "${fp}");
ext="${fn##*.}";
f="${fn%.*}";
new_fp="${fd}/${f}.avi"
mv -v "$fp" "$new_fp" 
done;

Это сработало для меня с помощью регулярных выражений:

Я хотел, чтобы файлы были переименованы так:

file0001.txt -> 1.txt
ofile0002.txt -> 2.txt 
f_i_l_e0003.txt -> 3.txt

usig регулярное выражение [az | _] + 0 * ([0-9] +.), где ([0-9] +.) - подстрока группы для использования в команде переименования.

ls -1 | awk 'match($0, /[a-z|\_]+0*([0-9]+.*)/, arr) { print   arr[0]  " "  arr[1] }'|xargs  -l mv

Производит:

mv file0001.txt 1.txt
mv ofile0002.txt 2.txt
mv f_i_l_e0003.txt 3.txt

Другой пример:

file001abc.txt -> abc1.txt
ofile0002abcd.txt -> abcd2.txt 

ls -1 | awk 'match($0, /[a-z|\_]+0*([0-9]+.*)([a-z]+)/, arr) { print   arr[0]  " "  arr[2] arr[1] }'|xargs  -l mv

Производит:

  mv file001abc.txt abc1.txt
  mv ofile0002abcd.txt abcd2.txt 

Предупреждение, будь осторожен.

Этот ответ представляет собой лишь конкретное применение того, что объяснено в других ответах, я надеюсь, что он будет полезен:

Я столкнулся с случаем, когда мне пришлось переименовать некоторые файлы свойств, которые имели формуCodeAPE_fr.propertiesкCodeAPE.properties, во всем каталоге, и я сделал:

      for i in `dir`; do mv $i `echo $i | sed 's/_fr.properties/.properties/g'`; done

Вот небольшое улучшение/корректировка, которая вам понадобится, если имена файлов содержат пробелы И вы хотите фильтровать на основе части имени файла, которая имеет шаблон с пробелами:

      for f in "Filename With Spaces"*; do mv "$f" "$(echo $f | sed 's/^Filename With Spaces/FilenameWithNoMoreSpaces/g')"; done

Ключевое отличие в моем ответе заключается в том, что вам нужно заключить в кавычки «Имя файла с пробелами», если это часть имени файла, которую вы хотите использовать в качестве фильтра для выбора файлов, с которыми нужно работать. В предыдущих ответах имя файла упоминалось с пробелами, но затем были выбраны файлы на основе jkl*, в котором нет пробелов.

Примечание. Заменять на FilenameWithNoMoreSpaces не обязательно. Я просто хотел показать типичный вариант использования в качестве примера.

Если шаблон с пробелом находится в СЕРЕДИНЕ имени файла, то измененная команда будет следовать следующему шаблону:

      for f in *"Spaces In Middle of Filename"*; do mv "$f" "$(echo $f | sed 's/Spaces In Middle of Filename/FilenameWithNoMoreSpaces/g')"; done

Обратите внимание: я добавил еще одну звездочку в начало шаблона имени файла с пробелами в двойных кавычках и удалил карат ^ из части «найти» команды sed. Мои извинения, если я слишком объясняю. Я принимаю во внимание, что некоторые люди, читающие это, могут быть совершенно новичками в программировании.

Вы также можете использовать приведенный ниже скрипт. на терминале очень легко бегать...

// Переименование нескольких файлов одновременно

for file in  FILE_NAME*
do
    mv -i "${file}" "${file/FILE_NAME/RENAMED_FILE_NAME}"
done

Пример:-

for file in  hello*
do
    mv -i "${file}" "${file/hello/JAISHREE}"
done
Другие вопросы по тегам