Xargs: изменить рабочий каталог на путь к файлу перед выполнением?
У меня есть большая папка с архивами RAR. Существует значительная иерархия уровней папок. Я хочу разархивировать всю коллекцию архивов одновременно.
У меня есть следующий однострочник, который будет работать:
find -name "*.rar" -print0 | xargs -0 -n 1 -P 4 unrar x
(Обратите внимание, что мы запускаем четыре потока параллельно, чтобы ускорить работу.:-))
Проблема этой команды в том, что xargs выполняется в каталоге верхнего уровня для каждого файла RAR. Это означает, что весь вывод выводится в папку верхнего уровня.
Вместо этого я хочу, чтобы выходные данные существовали в той же папке, что и архив RAR.
Пример:
Top level
|--FolderA
|----File1.rar
|----File2.rar
|--FolderB
|----File1.rar
|----File2.rar
|----File3.rar
|--FolderC
|----File1.rar
|----File2.rar
Каждый из файлов "File1.rar" содержит файл с тем же именем. Таким образом, извлечение их всех в папку верхнего уровня вызывает проблемы с перезаписью.
Подводя итог, я хочу извлечь все файлы RAR в вышеупомянутой иерархии. Я хочу, чтобы содержимое каждого файла RAR существовало в папке, в которой находится файл RAR.
Мне кажется, что решение состоит в том, чтобы как-то установить рабочий каталог, а затем запустить там форму команды unrar. Однако, так как команда find дает мне имена файлов, а не каталоги, я не могу сделать что-то вроде
| xargs -I{} -n 1 -P 4 cd {} \; unrar x {}
Если не считать написания сценариев Perl или Python, которые будут обтекать команду unrar и обрабатывать разбиение предоставленного пути на его части и выполнение команды, есть ли лучший способ добиться этого?
3 ответа
Существуют команды для извлечения имени каталога (dirname
) и имя файла (basename
) с пути. Так что вы могли бы сделать что-то вроде
find . -name '*.rar' -print0 | \
xargs -0 -I{} -n1 -P4 /bin/sh -c 'cd "$(dirname {})"; unrar x "$(basename {})"'
НАСКОЛЬКО МНЕ ИЗВЕСТНО, xargs
не поддерживает изменение каталогов, поэтому вам понадобится посредник, чтобы /bin/sh
, Вы упоминали, что писали обертку вокруг unrar
и это в основном то, что он делает, за исключением одной строки.
Используя GNU Parallel это выглядит так:
find . -name '*.rar' | parallel cd {//} '&&' unrar x {/}
GNU Parallel - это общий параллелизатор, который позволяет легко запускать задания параллельно на одной и той же машине или на нескольких машинах, к которым у вас есть доступ по ssh.
Если у вас есть 32 различных задания, которые вы хотите запустить на 4 процессорах, прямой способ распараллеливания - запустить 8 заданий на каждом процессоре:
GNU Parallel вместо этого порождает новый процесс после его завершения - поддерживая процессоры активными и, таким образом, экономя время:
Монтаж
Если GNU Parallel не упакован для вашего дистрибутива, вы можете выполнить личную установку, которая не требует root-доступа. Это можно сделать за 10 секунд, выполнив это:
(wget -O - pi.dk/3 || curl pi.dk/3/ || fetch -o - http://pi.dk/3) | bash
Для других вариантов установки см. http://git.savannah.gnu.org/cgit/parallel.git/tree/README
Учить больше
Смотрите больше примеров: http://www.gnu.org/software/parallel/man.html
Посмотрите вступительные видеоролики: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
Просмотрите учебник: http://www.gnu.org/software/parallel/parallel_tutorial.html
Подпишитесь на список рассылки, чтобы получить поддержку: https://lists.gnu.org/mailman/listinfo/parallel
Если вам не нужно -P
вариант из xargs
тогда вы можете использовать find -execdir
вариант, который как -exec
но делает cd
в каталог перед выполнением. Пример по адресу: https://stackoverflow.com/questions/16541582/finding-multiple-files-recursively-and-renaming-in-linux/54163971