Использование команды find для переименования файлов

У меня есть список файлов 10K mp4, некоторые из которых содержат? отметка. Мне нужно заменить их на _ (подчеркивание).

Я могу использовать команду find для поиска этих файлов, но не уверен, могу ли я их переименовать.

find . -name "*\?*.mp4" -exec mv {} XXXXXXX \; 

Похоже, мне нужно перебирать список, возвращаемый find, и для каждого из них мне нужно заменить? с _, но я не уверен, как это сделать.

Не используйте find для этого – какой бы способ вы ни find вам нужно что-то вроде одного mv на файл. Это много процессов и не приносит никакой пользы. Это не говоря уже о том, что это просто сложнее. Во всяком случае, это мое мнение.

Я бы использовал поток или пакетный инструмент и, как правило, предпочитаю pax :

 cd -P . && mkdir ../newmp4 && pax -rwls'|?|_|g' . "${PWD%/*}/newmp4" 

Если вы можете создать каталог ../newmp4 , этот маленький скрипт будет ../newmp4 , в которое ../newmp4 . в каталог, только что созданный с помощью hardlinks, но будет заменен каждый ? например, в любом имени файла с подчеркиванием.

Одним из преимуществ этого является то, что оба дерева продолжают существовать позже – ни один из зубных рядов, внедренных в текущий каталог для этих файлов, не пострадали – они только изменены в зеркальной версии дерева. И поэтому вы можете проверить результаты позже, прежде чем решить, какую версию дерева удалить. Если во время операции что-то не получится, – никакого вреда не будет.

Однако для этого требуется fs, который поддерживает hardlinks и который имеет по крайней мере столько оставшихся свободных inodes, сколько есть дочерних каталогов . +2, и предполагает, что .. и всех детей . совместно использовать одно и то же монтирование файловой системы.

Практически такой же эффект может быть получен с rsync , я думаю, хотя я не считаю, что это так же просто сделано. Если у вас нет pax (даже если вы действительно должны ) , вы можете просто получить его. mirabilos поддерживает (насколько мне известно) наиболее часто используемую версию на Linux-системах.

Лично я думаю, что это просто кричит для perl, специальный символ безопасен и переименован – это системный вызов не fork exec.

 find . -name "*\?*.mp4" -print0|perl -n0e 'next if /\?.*\//;$o=$_;s!\?!_!g;next if -e;rename $o, $_' 

Игнорирует файлы в каталогах? на их имя, чтобы предотвратить неопределенное поведение и проверки конфликтов переименования. См. Ниже подробное объяснение.

 find . \ # find files below the current directory -name "*\?*.mp4" \ # having a ? in the name and ending in .mp4 -print0\ # print the file names separated by nulls |perl \ # direct the output of find to the input of perl -n \ # loop over standard input while assigning it to $_ -0 \ # lines from standard input are separated by null -e \ # evaluate the next argument as perl commands 'next if /\?.*\//; # skip this file if the directory name includes ? $o=$_; # make a copy of the file name of preserve the name s!\?!_!g; # change the ?s in the filename to _s next if -e; # skip file if there is a file by that name rename $o, $_' # do the rename 

Вы можете использовать prename с помощью find :

 find . -type f -name '*\?*.mp4' -exec prename -n 's:(.*/[^?]*)\?(.*$):$1_$2:' {} + 

prename -n покажет файлы, которые будут переименованы, если вы удовлетворены тем, что запустите команду с удалением -n (то есть prename 's/\?/_/' ).

С zsh :

 autoload zmv # best in ~/.zshrc zmv '(**/)(*\?*.mp4)' '$1${2//\?/_}' 

Это исключает скрытые и не заглядывает в скрытые каталоги. Он будет переименовывать эти файлы независимо от их типа (регулярные, каталоги, символические ссылки …).

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

 zmv '(**/)(*\?*.(#i)mp4)(#qD)' '$1${2//\?/_}' 

Приближение GNU (не будет проверять наличие конфликтов и перезаписывать как zmv ):

 find . -iname '*\?*.mp4' -type f -exec bash -c ' for file do base=${file##*/} mv -i "$file" "${file%/*}/${base//\?/_}" done' {} + 

Самый простой способ, который я нашел для этого, – это подключиться к чему-то другому для создания команд, например sed , а затем выполнить команды в sh .

 find . -name '*\?*.mp4' -print | sed 's/.*/"&";h;y/?/_/;x;G;s/\n/ /;s/^/mv /' | sh -s 

find довольно понятна, как и sh . Команда sed может немного пояснить:

  • s/.*/"&"/ окружает вход с двойными кавычками для обработки любых пробелов или специальных символов;
  • h копирует буфер в буфер «hold», сохраняя копию;
  • y/?/_/ совпадает с tr \? _ tr \? _ в командной строке;
  • x заменяет содержимое буфера «hold» буфером шаблонов;
  • G добавляет буфер «hold» в конец буфера с разделителем '\ n'; теперь у нас есть «stringwith? \ nstringwith_»;
  • s/\n/ / заменяет новую строку;
  • s/^/mv / добавляет команду mv .

Это генерирует команды mv для каждого файла, найденного с помощью find со старым именем и новым именем.