2016-01-27 3 views
2

Я пытаюсь обрабатывать файлы DVD, которые находятся во многих разных местах на диске. Все, что у них общего, это то, что они (каждый набор входных файлов) находятся в каталоге с именем VIDEO_TS. Выходом в каждом случае будет один файл с именем родителя этого каталога.find piped to xargs with complex command

Я знаю, что я могу получить полный путь к каждому каталогу с:

find /Volumes/VolumeName -type d -name "VIDEO_TS" -print0 

и я могу получить родительский каталог по конвейеру к xargs:

find /Volumes/VolumeName -type d -name "VIDEO_TS" -print0 | xargs -0 -I{} dirname {} 

, и я также знаю, что может получить имя родительского каталога самостоятельно, добавив:

| xargs -o I{} basename {} 

Что я не могу понять как передать эти параметры, например, HandBrakeCLI:

./HandBrakeCLI -i /path/to/filename/VIDEO_TS -o /path/to/convertedfiles/filename.m4v 

я здесь прочитал о возможности расширения оболочки и подозревают, что собирается помочь здесь (не используя имя-каталога или базовое имя для начала), но чем больше я читал больше запутывается я получаю!

+0

'basename' вернет' имя_файла'/'имя_папки'. 'dirname' вернет путь к родительскому каталогу. – hek2mgl

+0

Что-то вроде: 'для video_ts в $ (find ...); do ./HandBrakeCLI -i $ {video_ts} -o /path/to/convertedfiles/filename.m4v; done' – ash

+0

Я получаю, как это приведет ко мне все файлы, чтобы действовать как vars (а затем запустить HandBrakeCLI на них), но механизм для извлечения (целевого) имени файла и его использования в цикле for еще не существует - если я чего-то не упущу. – Lorccan

ответ

3

Вы на самом деле не нужно xargs для этого на всех: Вы можете прочитать NUL- ограничивать поток в цикл оболочки и запускать команды, которые вы хотите прямо оттуда.

#!/bin/bash 

source_dir=/Volumes/VolumeName 
dest_dir=/Volumes/OtherName 

while IFS= read -r -d '' dir; do 
    name=${dir%/VIDEO_TS} # trim /VIDEO_TS off the end of dir, assign to name 
    name=${name##*/}  # remove everything before last remaining/from name 
    ./HandBrakeCLI -i "$dir" -o "$dest_dir/$name.m4v" 
done < <(find "$source_dir" -type d -name "VIDEO_TS" -print0) 

См the article Using Find на вики Грега или BashFAQ #001 для получения общей информации об обработке входных потоков в ударе, или BashFAQ #24 понять ценность использования процесса замещения (The <(...) конструктных здесь), а не обжигающе от поиска в петлю.


Кроме того, find содержит -exec действие, которое может быть использовано следующим образом:

source_dir=/Volumes/VolumeName 
dest_dir=/Volumes/OtherName 

export dest_dir # export allows use by subprocesses! 

find "$source_dir" -type d -name "VIDEO_TS" -exec bash -c ' 
    for dir; do 
    name=${dir%/VIDEO_TS} 
    name=${name##*/} 
    ./HandBrakeCLI -i "$dir" -o "$dest_dir/$name.m4v" 
    done 
' _ {} + 

Это передает найденные имена каталогов непосредственно в списке аргументов к оболочке вызывается с bash -c. Поскольку объект по умолчанию для цикла for для итерации - это "[email protected]", список аргументов, это неявно итерации по каталогам, найденным по find.

+0

Я узнал здесь что-то очень приятное. Что делает '_'? – ash

+1

@ash, '_' является заполнителем для' $ 0'. В противном случае вы получите там имя каталога VIDEO_TS, и оно будет пропущено в итерации над '' $ @ "'. –

+0

Имеет смысл, спасибо человеку – ash

2

Если я понимаю, что вы пытаетесь сделать, самым простым решением было бы создать небольшую обертку, которая принимает путь и вызывает ваш CLI:

Файл: CLIWrapper

#!/bin/bash 
for dir in "[email protected]"; do 
    ./HandBrakeCLI -i "${dir%/*}" -o "/path/to/convertedfiles/${dir##*/}.m4v" 
done 

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

./HandBrakeCLI -i "$dir" -o "/path/to/convertedfiles/${dir##*/}.m4v" 

или, возможно, что-то другое. Но теория верна. :)


Затем вы можете вызвать этот сценарий с помощью опции -exec в find. Скрипт перебирает свои аргументы, что делает его возможным для find отправить несколько аргументов в один вызов с использованием + терминатора:

find /Volumes/VolumeName -type d -name "VIDEO_TS" -exec ./CLIWrapper {} +