Xargs: изменить рабочий каталог на путь к файлу перед выполнением?

1285
fdmillion

У меня есть большая папка с архивами 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

2 ответа на вопрос

3
jjlin

There exist commands to extract a directory name (dirname) and file name (basename) from a path. So you could do something like

find . -name '*.rar' -print0 | \ xargs -0 -I{} -n1 -P4 /bin/sh -c 'cd "$(dirname {})"; unrar x "$(basename {})"' 

AFAIK, xargs doesn't support changing directories, so you would need some intermediary to do that, hence the /bin/sh. You mentioned writing a wrapper around unrar, and that is basically what this is doing, except in one-liner form.

Потрясающие! Именно то, что мне было нужно. Спасибо! fdmillion 9 лет назад 0
Это работает, если в именах путей нет пробелов, если есть пробелы, кажется, вам нужно набрать кавычки до нелепого уровня 'cd "$ (dirname" {} ")"; unrar x "$ (basename" {} ")" '(я использую unzip, а не unrar, но с той же разницей) junichiro 6 лет назад 0
1
Ole Tange

Using GNU Parallel it looks like this:

find . -name '*.rar' | parallel cd {//} '&&' unrar x {/} 

GNU Parallel is a general parallelizer and makes is easy to run jobs in parallel on the same machine or on multiple machines you have ssh access to.

If you have 32 different jobs you want to run on 4 CPUs, a straight forward way to parallelize is to run 8 jobs on each CPU:

Simple scheduling

GNU Parallel instead spawns a new process when one finishes - keeping the CPUs active and thus saving time:

GNU Parallel scheduling

Installation

If GNU Parallel is not packaged for your distribution, you can do a personal installation, which does not require root access. It can be done in 10 seconds by doing this:

(wget -O - pi.dk/3 || curl pi.dk/3/ || fetch -o - http://pi.dk/3) | bash 

For other installation options see http://git.savannah.gnu.org/cgit/parallel.git/tree/README

Learn more

See more examples: http://www.gnu.org/software/parallel/man.html

Watch the intro videos: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Walk through the tutorial: http://www.gnu.org/software/parallel/parallel_tutorial.html

Sign up for the email list to get support: https://lists.gnu.org/mailman/listinfo/parallel

Похожие вопросы