병렬을 사용하여 고유 한 입력 파일을 고유 한 출력 파일로 처리


18

입력 파일 (각 입력 줄이 많은 파일)로 가득 찬 디렉토리가 제공되는 쉘 스크립팅 문제가 있으며 각 출력을 고유 파일 (일명 file_1.input 필요)로 리디렉션하여 개별적으로 처리해야합니다. file_1.output에서 캡처되는 등).

사전 병렬 처리에서는 디렉토리의 각 파일을 반복하고 명령을 수행하면서 프로세서를 압도하지 않는 일종의 타이머 / 계산 기술을 수행합니다 (각 프로세스에 일정한 런타임이 있다고 가정). 그러나 항상 그런 것은 아니라는 것을 알고 있으므로 솔루션과 같은 "병렬"을 사용하면 사용자 지정 코드를 작성하지 않고 쉘 스크립트 멀티 스레딩을 얻는 가장 좋은 방법 인 것 같습니다.

이러한 각 파일을 처리하기 위해 병렬로 채울 수있는 방법을 생각했지만 코어를 효율적으로 관리 할 수 ​​있지만 모두 해킹 된 것처럼 보입니다. 나는 매우 쉬운 유스 케이스라고 생각하는 것을 가지고 있으므로 가능한 한 깨끗하게 유지하는 것을 선호합니다 (병렬 예제에서는 아무것도 내 문제가되는 것처럼 보이지 않습니다.

도움을 주시면 감사하겠습니다!

입력 디렉토리 예 :

> ls -l input_files/
total 13355
location1.txt
location2.txt
location3.txt
location4.txt
location5.txt

스크립트:

> cat proces_script.sh
#!/bin/sh

customScript -c 33 -I -file [inputFile] -a -v 55 > [outputFile]

업데이트 : 아래의 Ole의 답변을 읽은 후 필자의 병렬 구현을 위해 누락 된 부분을 모을 수있었습니다. 그의 대답은 훌륭하지만 여기에 내가 추가 한 연구와 메모가 있습니다.

전체 프로세스를 실행하는 대신 내 환경에서 자신의 솔루션을 입증하기 위해 개념 증명 명령으로 시작하는 것으로 나타났습니다. 내 두 가지 구현 및 참고 사항을 참조하십시오.

find /home/me/input_files -type f -name *.txt | parallel cat /home/me/input_files/{} '>' /home/me/output_files/{.}.out

find (문제가 될 수있는 ls 아님)를 사용하여 입력 파일 디렉토리에서 적용 가능한 모든 파일을 찾은 다음 해당 내용을 별도의 디렉토리 및 파일로 리디렉션합니다. 위의 내 문제는 읽고 리디렉션하는 것이 었습니다 (실제 스크립트는 간단했습니다). 스크립트를 cat로 바꾸는 것이 훌륭한 개념 증명이었습니다.

parallel cat '>' /home/me/output_files/{.}.out :::  /home/me/input_files/*

이 두 번째 솔루션은 병렬의 입력 변수 패러다임을 사용하여 파일을 읽지 만 초보자에게는 훨씬 더 혼란 스러웠습니다. 나를 위해, 찾기 및 파이프를 사용하면 내 요구가 잘 충족되었습니다.

답변:


27

GNU Parallel은 이러한 종류의 작업을 위해 설계되었습니다.

parallel customScript -c 33 -I -file {} -a -v 55 '>' {.}.output ::: *.input

또는:

ls | parallel customScript -c 33 -I -file {} -a -v 55 '>' {.}.output

CPU 코어 당 하나의 작업을 실행합니다.

다음과 같이 GNU Parallel을 간단하게 설치할 수 있습니다.

wget https://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem

자세한 내용은 GNU Parallel 소개 동영상을 참조하십시오 . https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


큰 대답 (및 병렬 사용 요청을 읽는 주요 요점).
J Jones

5

이 작업을 수행하는 표준 방법은 대기열을 설정하고 대기열에서 무언가를 가져 와서 처리하는 방법을 알고있는 많은 작업자를 생성하는 것입니다. 이러한 프로세스 간 통신에 fifo (일명 명명 된 파이프)를 사용할 수 있습니다.

아래는 그 개념을 보여주는 순진한 예입니다.

간단한 대기열 스크립트 :

#!/bin/sh
mkfifo /tmp/location-queue
for i in inputfiles/*; do
  echo $i > /tmp/location-queue
done
rm /tmp/location-queue

그리고 노동자 :

#!/bin/sh
while read file < /tmp/location-queue; do
  process_file "$file"
done

process_file 작업자 어딘가에 정의 할 수 있으며 필요한 모든 작업을 수행 할 수 있습니다.

이 두 조각이 있으면 큐 프로세스와 여러 작업자 프로세스를 시작하는 간단한 모니터를 가질 수 있습니다.

모니터 스크립트 :

#!/bin/sh
queue.sh &
num_workers="$1"
i=0
while [ $i < $num_workers ]; do
  worker.sh &
  echo $! >> /tmp/worker.pids
  i=$((i+1))
done
monitor_workers

거기 있어요 실제로이 작업을 수행하는 경우 모니터에서 fifo를 설정하고 대기열과 작업자로 경로를 전달하는 것이 좋습니다. 따라서 연결되지 않고 fifo의 특정 위치에 고정되지 않습니다. 구체적으로 답변 에이 방법을 설정 했으므로 읽을 때 사용중인 것이 분명합니다.


새로운 직원이 다음에 끝날 때까지 (일명, $ i가 감소하는 시점까지) 산란을 일시 정지 할 수있을 정도로 모니터는 어떻게 똑똑합니까? ---- 내 자신의 편집에 응답하여 작업자는 절대 떠나지 않으며 모든 처리가 끝날 때까지 파일을 처리합니다 (따라서 '프로세서'의 while 루프도 마찬가지입니다).
J Jones

모니터 스크립트가 끝날 때 "monitor_workers"라인은 무엇입니까?
J Jones

@JJones- monitor_workers그냥 process_file-당신이 원하는대로하는 기능입니다. 모니터에 관하여-당신이 옳았습니다. 작업자의 pid를 저장해야하므로 킬 신호를 보낼 수 있으며 작업자를 시작할 때 카운터를 늘려야합니다. 포함하도록 답변을 편집했습니다.
Shawn J. Goff

여러분의 작업에 진심으로 감사하지만, GNU를 사용해야한다고 생각합니다 parallel. 나는 그것이 당신의 아이디어라고 생각합니다.
motobói

5

또 다른 예:

ls *.txt | parallel 'sort {} > {.}.sorted.txt'

대부분의 경우 위의 내용이 검색했을 때 다른 예제가 불필요하게 복잡하다는 것을 알았습니다.


4

병렬화를 수행 할 수있는 일반적으로 사용 가능한 도구는 make입니다. GNU make와 다른 일부는 -j병렬 빌드를 수행 할 수있는 옵션이 있습니다.

.SUFFIXES: .input .output
.input.output:
        process_one_file <$< >$@.tmp
        mv -f $@.tmp $@

다음 make과 같이 실행하십시오 (파일 이름에 특수 문자가 포함되어 있지 않다고 가정합니다 make).

make -j 4 $(for x in *.input; do echo ${x%.*}.output; done)

imho 이것은 가장 영리한 해결책입니다 :)
h4unt3r

3

이것은 현재 디렉토리의 많은 파일 세트에서 동일한 명령을 수행하는 것입니다.

#!/bin/sh
trap 'worker=`expr $worker - 1`' USR1  # free up a worker
worker=0  # current worker
num_workers=10  # maximum number of workers
for file in *.txt; do
    if [ $worker -lt $num_workers ]; then
        {   customScript -c 33 -I -file $file -a -v 55 > `basename $file .txt`.outtxt 
            kill -USR1 $$ 2>/dev/null  # signal parent that we're free
        } &
        echo $worker/$num_worker $! $file  # feedback to caller
        worker=`expr $worker + 1`
    else
        wait # for a worker to finish
    fi
done

이것은 customScripttxt파일에서 실행되어 출력을 outtxt파일에 넣습니다 . 필요에 따라 변경하십시오. 이것을 작동시키는 열쇠는 SIGUSR1을 사용하는 신호 처리입니다. 그래서 자식 프로세스는 부모 프로세스에게 그것이 완료되었음을 알릴 수 있습니다. 스크립트의 대부분의 명령문이 쉘 스크립트에 대한 SIGCHLD 신호를 생성하므로 SIGCHLD를 사용하면 작동하지 않습니다. 나는 이것을 당신의 명령으로 바꾸려고 시도했다 sleep 1. 프로그램은 0.28 초의 사용자 CPU와 0.14 초의 시스템 CPU를 사용했다. 이것은 약 400 개 파일에만있었습니다.


'wait'는 현재 반복되는 동일한 파일을 가져 와서 형제 "if"문을 다시 입력 할 수있을 정도로 똑똑합니까?
J Jones

그것은 wait'똑똑한' 것이 아닙니다 . 그러나 SIGUSR1신호를 얻은 후에 돌아옵니다 . 자식 / 작업자는 SIGUSR1부모 에게 a 를 보내고 ,이 것은 잡히고 ( trap), 감소하고 $worker( trap절) 및에서 비정상적으로 반환 wait되어 if [ $worker -lt $num_workers ]절이 실행될 수 있도록합니다 .
Arcege

0

또는 xargs -P추가 소프트웨어를 설치할 필요없이 간단히 사용하십시오 .

find . -type f -print0 | xargs -0 -I'XXX' -P4 -n1 custom_script -input "XXX" -output "XXX.out"

옵션에 대한 약간의 설명 :

  • -I'XXX' 명령 템플릿에서 대체 될 문자열을 파일 이름으로 설정합니다.
  • -P4 4 개의 프로세스를 병렬로 실행합니다
  • -n1 두 개의 XXX를 찾더라도 실행 당 하나의 파일 만 넣습니다.
  • -print0-0작업 함께, 당신은 파일 이름 (공백 등) 특수 문자를시키는
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.