Как удалить (1) из имен файлов с помощью команды find

13

Недавно я конвертировал все свои файлы FLAC на более низкую частоту дискретизации 44,1 кГц и битовую глубину 24 бита (потому что iPhone / iPod не поддерживает ничего выше этого), используя XLD на моей Mac OS 10.7 (Lion).

Хотя я сказал XLD перезаписать все предыдущие файлы, XLD добавил (1)в конце самого файла, как из

some_song.m4a

в

some_song(1).m4a

Итак, теперь я хочу удалить это (1)из всех файлов FLAC, которые я конвертировал.

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

Я знаю, что find . -name *\(1\).m4aзахватит все преобразованные файлы FLAC.

Далее я знаю, что должен что-то сделать -execи mvпереименовать все найденные файлы. Но я не могу понять, как сохранить исходное имя файла и удалить только (1).

Может быть, мне нужно сделать захват некоторых групповых регулярных выражений, чтобы сохранить часть имени файла, которую я не хочу изменять? Или, возможно, невозможно сделать все в одной строке, и я должен создать сценарий оболочки (что мне не очень удобно, но я хочу попробовать).

Любые советы или предложения приветствуются! Благодарность!

hobbes3
источник
5
Почему отрицательный голос? Похоже, правильный вопрос ...
не связанный с вашим конкретным вопросом (о find), но который может решить вашу реальную проблему (преобразование аудиофайлов), вам может быть интересно взглянуть на audiotools.sourceforge.net и на этот пример (для macosx lion) invibe.net/ LaurentPerrinet / SciBlog / 2012-04-22
meduz

Ответы:

13

Не пытайтесь анализировать findвывод, кроме как в крайнем случае. Важно понимать, что в файловых системах Unix имена файлов - это не строки (распространенное заблуждение), а двоичные двоичные объекты, которые могут содержать любой символ, кроме /символа NULL. Парсинг имен файлов безопасно и корректно - это достаточно тяжело, так что в 99% случаев вам просто захочется вообще этого не делать (просто посмотрите, насколько волосатым является sedвыражение в ответе @ yarek, и даже если оно не охватывает все случаи) , К счастью, в этом случае есть гораздо более простой подход:

find . -name '*(1).m4a' -execdir sh -c \
'for arg; do mv "$arg" "${arg%(1).m4a}".m4a; done' _ {} \+
jw013
источник
аккуратный подход, но такой же волосатый, как выражение
собеседника
1
что-то здесь не хватает ... где done?
yrk
@yarek Спасибо за улов. Самая большая разница между этим и sedканальным подходом в том, что это намного безопаснее. Его все еще легче читать, чем суп с обратной косой чертой, если вы понимаете некоторые базовые конструкции сценариев оболочки.
jw013
Ты прав; это намного чище. И эта $argпеременная облегчает чтение.
hobbes3
1
@DQdlM Фрагмент, который я разместил выше, просто findвызывает shдовольно переносимый синтаксис POSIX. Для получения более подробной информации вы можете ознакомиться с разделом «Расширение параметров» , разделом о сложных командах, объясняющих forцикл , и спецификацией POSIXfind . В дополнение к этим ресурсам, я был бы рад ответить на любые ваши конкретные вопросы.
jw013
6

На Debian и Ubuntu я могу использовать rename 's/\(1\)//' *.m4aдля решения вашей проблемы.

Дом
источник
Странно, я могу сделать man rename, но на самом деле у меня нет rename: -bash: rename: command not found( which renameничего не показывает).
hobbes3
@ hobbes3 на Mac здесь man -a renameнаходит rename (2) - системный вызов, и rename (n) - команда TCL. Там нет ни переименования (1), ни самой утилиты.
yrk
1
IIRC, renameэто Perl-скрипт, включенный в примеры, включенные в некоторые установки Perl, и пара дистрибутивов включают его в путь, потому что это удобная команда. (@Hobbes, возможно, вы можете найти его в Интернете)
Кевин
@Kevin в моей системе rename- это обычный двоичный файл (не perl). Однако еще одно предостережение заключается в том, что не все версии renameподдерживают регулярные выражения. Например, у меня есть версия, которая позволяет вам выполнять прямую замену строк, например,rename '(1)' '' *.m4a
Патрик,
1
renameСкрипт Perl устанавливается только Debian и его производными. Другие системы Linux имеют другую renameутилиту (показанную Патриком). OSX не имеет ни того, ни другого.
Жиль "ТАК - перестань быть злым"
4

В zsh, используя zmv :

autoload zmv      # you can put this line in your .zshrc
zmv '(*)\(1\)(*)' '$1$2'

Во втором аргументе (новое имя) $1и $2ссылаются на заключенные в скобки группы (PATTERN)в исходном шаблоне. Еще один способ написания этого переименования

zmv '(*)' '${1/\(1\)/}'
Жиль "ТАК - прекрати быть злым"
источник
3

Следующий подход дает вам возможность предварительного просмотра / сокращения сгенерированных команд перед их выполнением, и он очень переносим: он должен работать не только на Mac, не только с bash, но и не только с GNU sed; даже в системах без команды find(1) можно duбез проблем заменить ее на (1).

find . -name '*(1).m4a' |
sed 's/\(.*\)(1).m4a$/mv & \1.m4a/' 

Если вы довольны напечатанными командами, перезапустите с | sh -xдобавлением.

Если вам нужны пробелы в именах файлов, добавьте еще один s для экранирования всех пробелов:

find . -name '*(1).m4a' |
sed -e 's/ /\\ /g' -e 's/\(.*\)(1).m4a$/mv & \1.m4a/'

Если ожидаются другие специальные символы, это немного сложнее:

find . -name '*(1).m4a' |
sed -e "s/'/'\\\\''/g" -e 's/\(.*\)(1).m4a$/mv '\''&'\'' '\''\1.m4a'\'/

Первая функция преобразует все 'в такую ​​форму, что они воспринимаются буквально в середине '...'строки -escaped. Вторая функция генерирует команды mv , аргументы которых заключены в '...'.

yrk
источник
1
Эта команда не экранирует имя файла (пробелы (и все другие специальные символы) и не добавляет кавычки вокруг имени файла. Я попытался изменить вашу команду, но я не мог понять, как добавить кавычки вокруг обоих имен файлов для mvкоманды. Один из полученного выхода из вашей команды есть mv ./The Beatles - Let It Be (MFSL LP 1-109) 24-96 Vinyl Rip/01 Two Of Us(1).m4a ./The Beatles - Let It Be (MFSL LP 1-109) 24-96 Vinyl Rip/01 Two Of Us.m4a. И если я выполню эту команду, я получу -bash: syntax error near unexpected token '('.
hobbes3
это должно быть домашнее задание :) но хорошо, я
обновлю
Кстати, GNU sed может выполнить саму команду, добавив eкоманду после подстановки. Например, find . -name '*(1).m4a' | sed 's/\(.*\)(1).m4a$/mv & \1.m4a/e'будет выполнять команду без передачи в sh -x.
Раш
@Rush это единственный сед, который делает это; и все равно нужно запустить оболочку, но новую для каждой команды (напоминает проблему make , не так ли?)
yrk
2
Я не думаю, что мы должны подавлять. В конце концов, это верное решение.
hobbes3
1

Вот небольшой скрипт, который делает это:

for var in `find . -type f -name "*(1).m4a"`; do
    new=`echo $var | cut -d'(' -f1`;
    mv $var $new.m4a;
done
Anupam
источник
этот будет задыхаться от файлов с пробелами в их именах; также требуется достаточно большое временное хранилище для`find ...`
yrk