find 명령에서 파이프 된 파일 이름 조작


10

나는 Bash에 비교적 익숙하지 않고 표면에서 매우 간단하게 보이는 것을 시도하고있다. 디렉토리 계층 구조를 찾아 모든 * .wma 파일을 가져 와서 파이프로 출력하는 명령을 mp3로 변환하고 변환 된 파일을 .mp3로 저장하십시오. 내 생각은 명령이 다음과 같아야한다는 것입니다 (오디오 변환 명령을 중단하고 대신 에코를 사용하여 설명합니다).

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

내가 이해했듯이 -print0 arg를 사용하면 공백이있는 파일 이름을 처리 할 수 ​​있습니다 (많은 음악 파일이기 때문에). 그런 다음 (xargs의 결과로) find의 각 파일 경로가 f로 캡처되고 문자열 끝에서 하위 문자열 일치 / 삭제를 사용하여 원래 파일 경로를 mp3로 에코해야한다고 기대합니다. wma 대신 확장. 그러나이 결과 대신 다음과 같은 결과가 나타납니다.

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

따라서 내 질문 (특정 '여기서 내가 잘못하고있는 것'은 제외하고) 파이프 작업의 결과 값은 변수 할당의 결과와 문자열 조작 작업에서 다르게 처리되어야합니다. ?


1
xargs와 함께 사용할 이유가 없습니다 find. -exec옵션 이 제공됩니다 . 질문에 사용할 명령을 추가하고 누군가에게 올바른 find명령을 보여줄 수 있습니까?
ixtmixilix

find 명령 (예 : {}멤버) 의 각 결과에 대해 문자열 조작을 수행 할 수있는 한, 예 (아래에 언급 한대로 )
Howard Dierking

실제로 xargs보다 적합한 경우가 exec있습니다. 사례에 대해서는이 스택 포스트 stackoverflow.com/questions/896808/find-exec-cmd-vs-xargs 를 참조하십시오 .
d34dh0r53

@ d34dh0r53 어떤 최첨단 사례? 연결하는 스레드가 아무 것도 가리 키지 않습니다.
Gilles 'SO- 악마 그만해'

답변:


8

다른 답변이 이미 식별했듯이 명령을 ${f%.*}실행하기 전에 셸에 의해 확장됩니다 xargs. 셸 변수 f를 파일 이름으로 설정하여 각 파일 이름에 대해이 확장을 한 번 -I f수행해야합니다 (전달 하지 않음 : xargs셸 변수에 대한 개념이 없으므로 f명령 에서 문자열 을 찾습니다). 예를 들어 ) xargs -I e echo …와 같은 명령을 실행했을 것 ./somedir/somefile.wmacho .mp3입니다.

이 접근 방식을 유지 xargs하면서 확장을 수행 할 수있는 쉘을 호출하도록 지시하십시오. 더 나은 정보 findxargs현대 버전의 find는 배관 문제가 적고 동일한 작업을 수행하는 구조를 갖기 때문에 대체로 사용되지 않는 도구이며 올바르게 사용하기가 어렵습니다. 대신을 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)을 실행한다고 가정하면 실제로 여기에서 사용할 필요는 없습니다 . 배쉬에 넣어 shopt -s globstar당신에 .bashrc활성화하기 위해 **/(KSH에서의 그 하위 디렉토리에서 같이 Recurse에 글로브 패턴 set -o globstar). 그럼 당신은 실행할 수 있습니다

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

(라는 디렉토리 가있는 경우 루프 시작 부분에 *.wma추가하십시오 [ -f "$f" ] || continue.)


훌륭한 설명뿐만 아니라 내가 깨닫지 못했던 방향으로 나를 지적했습니다 (globstar 기능을 얻기 위해 bash 셸 업그레이드)-결국 재귀 globbing은 명령을 단순하게 유지하고 내가하려고하는 모든 것을 달성 한 솔루션이었습니다. . 감사!
Howard Dierking

6

솔루션 평가 $ {f %. *}. mp3의 경우 xargs로 분기 된 쉘이 아니라 전체 명령을 호출하는 쉘에서 수행됩니다 . 그리고 쉘에는 f 변수 가 없으며 빈 문자열로 대체됩니다.

-I f 와 함께 xargs 를 사용하는 솔루션 :

% 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,'
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.