Маніпулюйте ім'ям файлу, що знаходиться в команді find


10

Я відносно новий в Bash і намагаюся зробити щось, що на поверхні здавалося досить простим - запустіть пошук над ієрархією каталогів, щоб отримати всі * .wma файли, що передають у команду, де я конвертую їх у mp3 та збережіть перетворений файл як .mp3. Я думав, що команда повинна виглядати наступним чином (я припинив команду перетворення звуку і замість цього використовую ехо для ілюстрації):

$ find ./ -name '*.wma' -type f -print0 | xargs -0 -I f echo ${f%.*}.mp3

Як я розумію, аргумент -print0 дозволить мені обробляти імена файлів, які мають пробіли (які багато з них роблять так, як це музичні файли). Тоді я очікую (в результаті xargs), що кожен шлях до знаходження файлу буде захоплений f, і що, використовуючи відповідність підрядків / видалити з кінця рядка, я повинен відповідати початковому шляху файлу mp3 розширення замість wma. Однак замість цього результату я бачу таке:

*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
...

Отже, моє запитання (окрім конкретного "що я тут неправильно роблю"), це таке - чи не слід, щоб значення, що є результатом роботи труби, трактувались в операціях маніпулювання рядками, ніж ті, що є результатом призначення змінної ?


1
Там немає ніяких підстав для використання xargsз find. Він поставляється з -execваріантом. Чи можете ви просто додати до запитання команду, яку ви будете використовувати, і хтось може показати вам правильну findкоманду?
ixtmixilix

так (як я зазначив нижче), доки я все ще можу робити маніпуляції з рядком для кожного результату команди find (наприклад, {}члена)
Howard Dierking

насправді є крайові випадки, коли xargsце більше підходить, ніж exec. Дивіться цей stackpost stackoverflow.com/questions/896808/find-exec-cmd-vs-xargs для даного випадку.
d34dh0r53

@ d34dh0r53 Який крайовий регістр? Нитка, на яку ви посилаєтесь, не вказує жодної.
Жил "ТАК - перестань бути злим"

Відповіді:


8

Як вже були визначені інші відповіді, ${f%.*}вона розгортається оболонкою до запуску xargsкоманди. Потрібно, щоб це розширення відбулося один раз для кожного імені файлу, при цьому змінна оболонки fвстановлена ​​на ім'я файлу (передача -I fцього не робить: xargsне має поняття змінної оболонки, вона шукає рядок fу команді, тож якщо ви хочете використовується, наприклад, xargs -I e echo …вона виконала б такі команди, як ./somedir/somefile.wmacho .mp3).

Дотримуючись такого підходу, скажіть xargsвикликати оболонку, яка може виконувати розширення. Краще скажіть find- xargsце значною мірою застарілий інструмент і його важко правильно використати, оскільки сучасні версії знаходження мають конструкцію, яка виконує ту саму роботу (і більше) з меншими труднощами з водопроводу. Замість того find … -print0 | xargs -0 command …, щоб бігти find … -exec command … {} +.

find . -name '*.wma' -type f -exec sh -c 'for f; do echo "${f%.*}.mp3"; done' _ {} +

Аргумент _знаходиться $0в оболонці; імена файлів передаються як позиційні аргументи, які for f; do …перетворюються на цикл. Простіша версія цієї команди виконує окрему оболонку для кожного файлу, яка еквівалентна, але трохи повільніше:

find . -name '*.wma' -type f -exec sh -c 'echo "${0%.*}.mp3"' {} \;

Тут вам фактично не потрібно користуватися find, припускаючи, що ви використовуєте досить недавню оболонку (ksh93, bash ≥4.0 або zsh). У bash, shopt -s globstarвведіть у свою, .bashrcщоб активувати **/шаблон шаблону для повторного запису в підкаталогах (у ksh, це set -o globstar). Тоді можна бігти

for f in **/*.wma; do
  echo "${f%.*}.mp3"
done

(Якщо у вас є виклики каталогів *.wma, додайте [ -f "$f" ] || continueна початку циклу.)


чудові пояснення, а також вказали на мене в напрямку, якого я навіть не зрозумів (модернізувавши мою баш-коробку, щоб отримати функцію globstar) - врешті-решт, рекурсивний глобулінг був тим рішенням, яке полегшило команду і виконало все, що я намагався зробити. . Дякую!
Говард Діркінг

6

У разі вашої оцінки рішення $ {f%. *}. Mp3 виконується в оболонці, в яку ви викликаєте всю команду, а не в оболонці, роздвоєній xargs . І у вашій оболонці немає змінної f , вона заміщена порожнім рядком.

Рішення з використанням xargs з -I f :

% cat 1.sh 
#!/bin/sh
f=$1
echo ${f%.*}.mp3
% /bin/ls -1
1.sh
aaa.wma
bbbb.wma
ccccc.wma
% find ./ -name '*.wma' -type f -print0 | xargs -0 -I f ./1.sh f
./aaa.mp3
./ccccc.mp3
./bbbb.mp3

Але я зробив би це так:

% find ./ -name '*.wma' -type f | sed 's,\.wma$,.mp3,'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.