bash найти exec: переименовать файлы и сохранить необязательные расширения? - PullRequest
0 голосов
/ 11 января 2019

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

Желаемое преобразование:

tools-win/foo.exe => tools-win/foo_bar.exe
tools-osx/foo     => tools-osx/foo_bar

Есть ли способ сделать это с помощью выражений параметров bash? Моя текущая попытка:

find . -path 'tools-*/*' \
    -execdir sh -c 'mv "$1" "${1%.*}_$2.${1##.}"' _ {} bar \;`

Это работает с файлами, имеющими расширения, но захватывает весь префикс для файлов без расширений:

tools-win/foo.exe => tools-win/foo_bar.exe
tools-osx/foo     => tools-osx/foo_bar.foo

Можно ли здесь использовать одно выражение параметра, которое обрабатывает оба случая?

Ответы [ 3 ]

0 голосов
/ 12 января 2019

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

PREFIX=${1%.*}
SUFFIX=${1##"${PREFIX}"}
FINAL=${PREFIX}_bar${SUFFIX}

Обратите внимание, что если ${PREFIX} идентична входной строке, ${SUFFIX} будет пустым.

Так что, хотя внутри sh он не является однострочным, вызов, который вызывает find -exec только один раз без каналов, выглядит следующим образом:

find "${PATH_TO_SEARCH}" -path "${PATH_TO_SEARCH}/tools-*/*" \
    -execdir sh -c 'p=${1%.*};s=${1##"${p}"};mv "$1" "${p}_$2${s}"' sh {} bar \;`
0 голосов
/ 12 января 2019

Вы можете сделать это очень просто с помощью rename, a.k.a. Perl rename. Он маскируется под разными именами в разных менеджерах пакетов / операционных системах.

У него есть переключатель -X, который удаляет расширение, а затем добавляет его обратно, и переключатель -a, который позволяет добавлять строки в качестве постфиксов к текущему имени. Таким образом, чтобы добавить _bar к базовой части имени файла перед расширением, для всех файлов PNG используйте:

rename -X -a "_bar" *.png

Он может сделать еще миллион вещей:

  • добавьте --dry-run, чтобы увидеть, что он будет делать, фактически ничего не делая, это прекрасно
  • используйте $N для введения последовательного счетчика в имена файлов
  • автоматически обнаруживает коллизии, когда два изменения могут привести к перезаписи файла, и предупреждает вас перед нанесением какого-либо вреда - это неоценимо
  • вы можете передать ему целые сценарии Perl для обработки имен файлов самым сложным способом, который вы можете себе представить

Вы можете проверить, является ли ваш rename Perl, к которому я обращаюсь:

file $(which rename)

и если он верен, вы увидите, что это исполняемый файл Perl. Если вы найдете правильный вариант для своей ОС, возможно, вы добавите комментарий к этому ответу, в котором будет сказано, какой пакет вы установили, чтобы получить его.


В macOS вы можете установить инструмент rename, о котором я говорю, с помощью homebrew следующим образом:

brew install rename 
0 голосов
/ 11 января 2019

Первый упрощенный проход:

отформатированный:

find | 
  while read -r f
  do case $f in
     *.???) echo mv "$f" "${f%.???}_bar${f##${f%.???}}" ;;
         *) echo mv "$f" "${f}_bar"                     ;;
     esac
  done

Волшебный беспорядок "${f%.???}_bar${f##${f%.???}}" -
${f%.???} - это имя файла без упрощенно определенного расширения. ${f##${f%.???}} - это упрощенно определенное расширение, используемое для удаления всего иначе из начала файла, поэтому является (сложно полученным) вышеупомянутым «упрощенно определяемым расширением». :)

Это не должно создавать никаких подоболочек (кроме find), поэтому оно должно быть очень эффективным.

<Ч />

CAVEAT SCRIPTOR

Существенным недостатком является .???, который, скорее всего, НЕ будет делать то, что вы хотели, когда он совпадет с именами файлов x.v1.2.3 и переименует его в x.v1_bar.2.3. Скорее всего, это можно исправить, но, не видя ваших данных, я не хотел неправильно их уточнять.

<Ч />

Тем не менее, можно сжать до одной строки -

  find | while read -r f; do case $f in *.???) echo mv "$f" "${f%.???}_bar${f##${f%.???}}" ;; *) echo mv "$f" "${f}_bar";; esac; done

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...