Как я могу изменить путь, исходящий от {} в `find -exec`?

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

Чего хочу добиться. Копировать все *.jpg файлы из каждого каталога в один каталог. Чтобы избежать перезаписи, я намерен добавить или, скорее, вставить время эпохи в имя файла.

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

find . -type f -name '*.jpg' -exec cp -T {} /media/hvdw/'Local station'/jpg/{}-$(date +%s) ;

Он отлично работает в одном каталоге, но как cp команда включает путь, я получаю следующую ошибку:

cp: cannot create regular file '/media/hvdw/Local station/jpg/./VXIMAGES/2999  03.jpg-1618003427': No such file or directory
  • Вопрос первый: как я могу скопировать файл с пустым именем?
  • Вопрос второй: как я могу вставить эпоху внутри имени файла и не добавлять ее к расширению? нравиться 2999 03-1618003427.jpg.

1 ответ
1

Как вы обнаружили, -exec заменители {} с полным путем, используемым find.

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

Вы запускаете оболочку из find как это:

find … -exec sh -c '…' …

где аргумент в одинарных кавычках сразу после sh -c содержит код оболочки. Вы должны знать, как это сделать безопасно. Никогда не вставлять {} в коде оболочки. В вашем случае команда может быть такой:

find . -type f -name '*.jpg' -exec sh -c '…' find-sh {} ;

Примечание: find-sh объясняется здесь: Какая вторая шишка в sh -c 'some shell code' sh?

Теперь нам просто нужен правильный код оболочки, который будет манипулировать именем пути и вызывать cp. Этот мой ответ дает способ удалить некоторый суффикс, синтаксис будет похож на ${variable%.jpg} в нашем случае. Ссылки там ведут в места, где задокументирован синтаксис для удаления некоторого префикса. Мы будем использовать ${variable##*/} чтобы сначала получить базовое имя.

find . -type f -name '*.jpg' -exec sh -c '
   file="$1"
   newname="${file##*/}"
   newname="${newname%.jpg}"
   cp -T -- "$file" "/media/hvdw/Local station/jpg/$newname-$(date +%s).jpg"
' find-sh {} ;

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

Приведенный выше код запускает один sh и один cp на соответствующий файл. Мы можем улучшить производительность, уменьшив количество sh процессы. В следующем варианте используется -exec sh … {} + передать несколько имен пути sh:

find . -type f -name '*.jpg' -exec sh -c '
for file do
   newname="${file##*/}"
   newname="${newname%.jpg}"
   cp -T -- "$file" "/media/hvdw/Local station/jpg/$newname-$(date +%s).jpg"
done
' find-sh {} +

Заключительные примечания:

  • Достаточно быстро cp вызовет вывод из date +%s быть неуникальным. На всякий случай рассмотрим date +%s.%N если твой date поддерживает %N.
  • -name '*.jpg' чувствителен к регистру и .jpg дважды жестко запрограммирован в нашем коде оболочки. Если вы каким-то образом разрешаете другие расширения (например, используя непереносимые, нечувствительные к регистру -iname '*.jpg' вместо этого) вам нужно будет улучшить код оболочки, чтобы обрабатывать заранее неизвестные расширения. Это возможно, но я не буду здесь вдаваться в подробности *.
  • Двойное тире в cp -T -- "$file" … кажется ненужным, потому что расширенный $file не может начинаться с - потому что это должно начинаться с . потому что мы использовали find . …. Но
    • очевидно, не все реализации find работать так;
    • даже если твой find делает, когда-нибудь вы можете захотеть использовать код с find * … или так, то может получиться развернутое $file начинается с -.

*) Вы или кто-либо другой можете задать отдельный вопрос, если это необходимо.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *