Bash: 교착 상태 없이 파이프 연결

Bash: 교착 상태 없이 파이프 연결

여러 개의 파일 이름을 나열하고 각 입력 이름에 대해 새 이름을 출력하는 find유틸리티(라고 부르겠습니다 util)를 통해 파이프한 다음 각 파일의 이름을 이전 이름에서 새 이름으로 바꾸고 싶습니다.

가장 기본적인 해결책은 다음과 같습니다.

find . -print0 | while IFS= read -d '' -r old_name; do
    new_name="$(echo "$file" | util)"
    mv "$old_name" "$new_name"
done

이 접근 방식의 문제점은 util각 파일 이름을 개별적으로 실행하기에는 너무 느리다는 것입니다. 따라서 해결책은 util한 번만 시작하고 이 단일 프로세스를 통해 모든 파일 이름을 전송하는 것입니다.

find . -print0 >old_names
util <old_names >new_names

exec {old_fd}<old_names
exec {new_fd}<new_names

while IFS= read -d '' -r old_name <&$old_fd &&
      IFS= read -d '' -r new_name <&$new_fd; do
    mv "$old_name" "$new_name"
done

이것은 util한 번만 시작되지만 더 이상 파이프가 아닙니다. 모든 파일을 tmp 파일에 나열하고 util이 tmp 파일을 실행하여 다른 tmp 파일을 가져온 다음 실제로 이름 바꾸기를 시작해야 합니다. .

파이프라인 방식으로 이 작업을 수행하기 위해 다음을 시도했습니다.

mkfifo old_names new_names
find . -print0 | tee old_names | util >new_names &

exec {old_fd}<old_names
exec {new_fd}<new_names

while IFS= read -d '' -r old_name <&$old_fd &&
      IFS= read -d '' -r new_name <&$new_fd; do
    mv "$old_name" "$new_name"
done

util불행히도 이것은 입력/출력이 버퍼링되는 방식 에 따라 교착 상태가 될 수 있습니다 .

그래서 제 질문은: bash에서 이 작업을 수행하는 올바른 방법은 무엇입니까?

답변1

파이프를 연결하지 않는 솔루션:

find . -print > infiles
cat infiles | util > outfiles
parallel mv ::::+ infiles outfiles

장점: 매우 간단합니다. 단점: 이름 바꾸기는 util완료 후에만 시작됩니다. 임시 파일 2개.

find . -print > infiles
cat infiles | util | parallel -j1 mv ::::+ infiles -

장점: 이름 지정이 util시작되면 이름 바꾸기도 시작됩니다. 단점: 임시 파일이 1개입니다.

find . -print | util | parallel -j1 mv ::::+ <(find . -print) -

장점: 이름 지정이 util시작되면 이름 바꾸기도 시작됩니다. 단점: 현재 디렉터리를 변경할 필요가 없습니다. 그렇지 않으면 두 finds가 동일한 결과를 제공하지 않을 수 있습니다.

답변2

변환(util)의 복잡성에 따라 이름 바꾸기(Larry Wall의 이름 바꾸기)를 사용할 수 있습니다.

또는

util출력 이름 쌍을 정렬합니다 : old-name new-name, 그런 다음

find . -print0 | util | xargs -n2 mv -T

또는

이런 것을 시도해 보세요.

function a() { 
    echo -n "$1 "
    echo "$1" | sed -e s/a/b/g
}

… | while read arg; do
a "$arg"
done

관련 정보