sử dụng song song để xử lý các tệp đầu vào duy nhất thành các tệp đầu ra duy nhất


18

Tôi gặp vấn đề về kịch bản shell khi tôi được cung cấp một thư mục chứa đầy các tệp đầu vào (mỗi tệp chứa nhiều dòng đầu vào) và tôi cần xử lý chúng riêng lẻ, chuyển hướng từng đầu ra của chúng sang một tệp duy nhất (còn gọi là file_1.input được ghi lại trong tệp_1.output, v.v.).

Song song , tôi sẽ chỉ lặp lại từng tệp trong thư mục và thực hiện lệnh của mình, trong khi thực hiện một số loại kỹ thuật hẹn giờ / đếm để không áp đảo các bộ xử lý (giả sử rằng mỗi quy trình có thời gian chạy không đổi). Tuy nhiên, tôi biết rằng sẽ không phải luôn luôn như vậy, vì vậy sử dụng giải pháp "song song" có vẻ là cách tốt nhất để có được chuỗi đa luồng shell mà không cần viết mã tùy chỉnh.

Trong khi tôi đã nghĩ ra một số cách để xử lý song song để xử lý từng tệp này (và cho phép tôi quản lý các lõi của mình một cách hiệu quả), tất cả chúng đều có vẻ hack. Tôi có những gì tôi nghĩ là một trường hợp sử dụng khá dễ dàng, vì vậy tôi muốn giữ nó sạch nhất có thể (và không có gì trong các ví dụ song song dường như là vấn đề của tôi.

Bất kỳ trợ giúp sẽ được đánh giá cao!

ví dụ thư mục đầu vào:

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

Kịch bản:

> cat proces_script.sh
#!/bin/sh

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

Cập nhật : Sau khi đọc câu trả lời của Ole bên dưới, tôi đã có thể tập hợp các phần còn thiếu để thực hiện song song của riêng mình. Trong khi câu trả lời của anh ấy rất hay, đây là nghiên cứu bổ sung và ghi chú của tôi, tôi đã thực hiện:

Thay vì chạy toàn bộ quá trình của mình, tôi đã tìm cách bắt đầu với một bằng chứng về lệnh khái niệm để chứng minh giải pháp của anh ấy trong môi trường của tôi. Xem hai triển khai khác nhau của tôi (và ghi chú):

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

Sử dụng find (không phải ls, có thể gây ra sự cố) để tìm tất cả các tệp áp dụng trong thư mục tệp đầu vào của tôi, sau đó chuyển hướng nội dung của chúng sang một thư mục và tệp riêng biệt. Vấn đề của tôi từ phía trên là đọc và chuyển hướng (kịch bản thực tế rất đơn giản), vì vậy thay thế kịch bản bằng con mèo là một bằng chứng tốt về khái niệm.

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

Giải pháp thứ hai này sử dụng mô hình biến đầu vào song song để đọc các tệp trong, tuy nhiên đối với người mới, điều này khó hiểu hơn nhiều. Đối với tôi, sử dụng find a và pipe đáp ứng nhu cầu của tôi tốt.

Câu trả lời:


27

GNU Parallel được thiết kế cho loại nhiệm vụ này:

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

hoặc là:

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

Nó sẽ chạy một công việc trên mỗi lõi CPU.

Bạn có thể cài đặt GNU Parallel đơn giản bằng cách:

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

Xem các video giới thiệu về GNU Parallel để tìm hiểu thêm: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


Câu trả lời tuyệt vời (và những điểm chính để đọc yêu cầu sử dụng song song của tôi).
J Jones

5

Cách tiêu chuẩn để làm điều này là thiết lập một hàng đợi và sinh ra bất kỳ số lượng công nhân nào biết cách lấy thứ gì đó từ hàng đợi và xử lý nó. Bạn có thể sử dụng fifo (còn được gọi là ống) để liên lạc giữa các quy trình này.

Dưới đây là một ví dụ ngây thơ để chứng minh khái niệm.

Một kịch bản hàng đợi đơn giản:

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

Và một công nhân:

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

process_file có thể được định nghĩa ở đâu đó trong nhân viên của bạn và nó có thể làm bất cứ điều gì bạn cần.

Khi bạn có hai phần đó, bạn có thể có một màn hình đơn giản khởi động quy trình xếp hàng và bất kỳ số lượng quy trình công nhân nào.

Kịch bản giám sát:

#!/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

Có bạn có nó. Nếu bạn thực sự làm điều này, tốt hơn là thiết lập fifo trong màn hình và chuyển đường dẫn đến cả hàng đợi và công nhân, để chúng không bị ghép và không bị kẹt vào một vị trí cụ thể cho fifo. Tôi thiết lập nó theo cách này trong câu trả lời một cách cụ thể để rõ ràng rằng những gì bạn đang sử dụng khi bạn đọc nó.


Làm thế nào màn hình đủ thông minh để tạm dừng sinh sản đối với nhân viên mới cho đến khi lần tiếp theo kết thúc (hay còn gọi là $ i bao giờ bị giảm)? ---- Trả lời chỉnh sửa của riêng tôi, các công nhân không bao giờ biến mất, họ chỉ xử lý các tệp cho đến khi tất cả quá trình xử lý đã hết (do đó vòng lặp while trong 'bộ xử lý' cũng vậy).
J Jones

Dòng "Monitor_workers" ở cuối tập lệnh màn hình đang hoạt động là gì?
J Jones

@JJones - monitor_workerscũng giống như process_file- đó là một chức năng làm bất cứ điều gì bạn muốn. Về màn hình - bạn đã đúng; nó nên lưu các pids của công nhân của nó (để nó có thể gửi tín hiệu tiêu diệt) và bộ đếm cần được tăng lên khi nó khởi động một worker. Tôi đã chỉnh sửa câu trả lời để bao gồm điều đó.
Shawn J. Goff

Tôi thực sự đánh giá cao công việc của bạn, nhưng tôi nghĩ bạn nên sử dụng GNU parallel. Tôi nghĩ đó là ý tưởng của bạn, thực hiện đầy đủ.
motobói

5

Một vi dụ khac:

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

Tôi tìm thấy các ví dụ khác phức tạp không cần thiết, trong hầu hết các trường hợp ở trên là những gì bạn có thể đã tìm kiếm.


4

Một công cụ thường có sẵn có thể làm song song hóa được thực hiện. GNU make và một vài cái khác có -jtùy chọn để thực hiện các bản dựng song song.

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

Chạy makenhư thế này (Tôi giả sử tên tệp của bạn không chứa bất kỳ ký tự đặc biệt nào, makekhông tốt với những ký tự đó):

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

imho đây là giải pháp thông minh nhất :)
h4unt3r

3

Điều này là để thực hiện cùng một lệnh trên một tập hợp lớn các tệp trong thư mục hiện tại:

#!/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

Điều này chạy customScripttrên mỗi txttệp, đặt đầu ra trong outtxtcác tệp. Thay đổi khi bạn cần. Chìa khóa để làm việc này là xử lý tín hiệu, sử dụng SIGUSR1 để tiến trình con có thể cho tiến trình cha mẹ biết rằng nó đã được thực hiện. Sử dụng SIGCHLD sẽ không hoạt động vì hầu hết các câu lệnh trong tập lệnh sẽ tạo tín hiệu SIGCHLD cho tập lệnh shell. Tôi đã thử điều này thay thế lệnh của bạn bằng sleep 1, chương trình đã sử dụng 0,28s cpu người dùng và 0,14s cpu hệ thống; đây chỉ là khoảng 400 tập tin.


Làm thế nào là 'chờ' đủ thông minh để lấy cùng một tệp hiện đang được lặp đi lặp lại và nhập lại câu lệnh "if" của anh chị em?
J Jones

Nó không waitđủ 'thông minh'; nhưng nó sẽ trở lại sau khi nhận được SIGUSR1tín hiệu. Đứa trẻ / công nhân gửi a SIGUSR1cho cha mẹ, được bắt ( trap) và giảm $worker( trapmệnh đề) và trả lại bất thường từ wait, cho phép if [ $worker -lt $num_workers ]mệnh đề được thực thi.
Arcege

0

Hoặc đơn giản là sử dụng xargs -P, không cần cài đặt phần mềm bổ sung:

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

Một chút giải thích cho các tùy chọn:

  • -I'XXX' đặt chuỗi sẽ được thay thế trong mẫu lệnh bằng tên tệp
  • -P4 sẽ chạy song song 4 tiến trình
  • -n1 sẽ chỉ đặt một tệp cho mỗi lần thực hiện mặc dù đã tìm thấy hai XXX
  • -print0-0làm việc cùng nhau, cho phép bạn có các ký tự đặc biệt (như khoảng trắng) trong tên tệp
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.