使用并行将唯一的输入文件处理为唯一的输出文件


18

我遇到了一个shell脚本问题,在该脚本中,我给了一个充满输入文件的目录(每个文件包含许多输入行),我需要单独处理它们,将它们的每个输出重定向到唯一文件(又名file_1.input需要)。在file_1.output中捕获,依此类推)。

Pre-parallel之前,我将遍历目录中的每个文件并执行我的命令,同时使用某种计时器/计数技术以免使处理器不堪重负(假设每个进程的运行时间恒定)。但是,我知道情况并非总是如此,因此使用类似“并行”的解决方案似乎是无需编写自定义代码即可获得Shell脚本多线程的最佳方法。

尽管我已经想到了一些方法来并行处理这些文件(并允许我高效地管理内核),但它们似乎都是很棘手的。我有一个非常简单的用例,因此希望尽可能保持整洁(并且并行示例中的任何内容似乎都不是我的问题。

任何帮助,将不胜感激!

输入目录示例:

> 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/*

第二种解决方案使用parallel的输入变量范例读取文件,但是对于新手来说,这更令人困惑。对我来说,使用find和pipe可以满足我的需求。

Answers:


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琼斯

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琼斯

监视脚本执行的末尾的“ monitor_workers”行是什么?
J琼斯

@JJones- monitor_workers就像process_file-它是一个可以执行您想要的任何功能的函数。关于显示器-您是对的;它应该保存其工作线程的pid(以便它可以发送终止信号),并且在启动工作线程时需要增加计数器。我已经编辑了答案以包括该答案。
肖恩·高夫

非常感谢您的工作,但我认为您应该使用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)

恕我直言,这是最聪明的解决方案:)
h4unt3r 2014年

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

这将customScript在每个txt文件上运行,将输出放入outtxt文件中。根据需要进行更改。使之起作用的关键是使用SIGUSR1进行信号处理,以便子进程可以让父进程知道它已经完成。使用SIGCHLD无效,因为脚本中的大多数语句都会向外壳程序脚本生成SIGCHLD信号。我尝试用替换您的命令sleep 1,程序使用0.28s用户cpu和0.14s系统cpu;这只有大约400个文件。


如何使“等待”足够聪明,以获取当前正在迭代的同一文件并重新输入同级“ if”语句?
J琼斯

这是不是wait就是“智能”就够了; 但它会在收到SIGUSR1信号后返回。子代/工人向SIGUSR1父代发送一个,该父代被捕获(trap),并递减$workertrap子句)并从中异常返回wait,从而允许该if [ $worker -lt $num_workers ]子句执行。
Arcege'2

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一起工作,让你在文件名中的特殊字符(如空格)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.