在BASH脚本中,我们可以在后台运行多个进程,这些进程使用命名管道进行互通,在文件系统上注册FIFO。这方面的一个例子可能是:
#!/bin/bash
mkfifo FIFO
# BG process 1
while :; do echo x; done & >FIFO
# BG process 2
while :; do read; done & <FIFO
exit
我想知道是否可以在不使用文件系统上的FIFO的情况下在脚本的后台进程之间进行相同的相互通信,也许可以使用某种文件描述符重定向。
答案 0 :(得分:16)
这是一个运行两个子进程的示例,这两个子进程实现为相同shell脚本的函数...一个子进程生成数字1 ... 5(在打印之间休眠),第二个从固定文件描述符读取(5,到将第一个FD的STDOUT重定向到),乘以2再次打印。主进程将第二个进程的STDOUT重定向到另一个固定的文件描述符(6),然后再循环读取该循环中的那个进程。
它与使用pipe(2)系统调用创建的fd对的C代码基本相同。要理解发生了什么,请在strace -f!
下运行脚本Bash Version是在Ubuntu / x86上运行的4.2.24(1)。
[ubuntu /home/chris]
$ bash --version
GNU bash, version 4.2.24(1)-release (i686-pc-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
脚本输出:
[ubuntu /home/chris]
$ ./read_from_fd.sh
Got number 2.
Got number 4.
Got number 6.
Got number 8.
Got number 10.
源代码:
#!/bin/bash
# Generate data, output to STDOUT.
generate_five_numbers() {
for n in `seq 5` ; do
echo $n
sleep 2
done
}
# Read data from FD#5, multiply by two, output to STDOUT.
multiply_number_from_fd5_by_two() {
while read n <&5 ; do
echo "$(( $n * 2 ))"
done
}
# choose your FD number wisely ;-)
# run generator with its output dup'ed to FD #5
exec 5< <( generate_five_numbers )
# run multiplyier (reading from fd 5) with output dup'ed to FD #6
exec 6< <( multiply_number_from_fd5_by_two )
# read numbers from fd 6
while read n <&6 ; do
echo "Got number $n."
done
运行时处理树:
──read_from_fd.sh(8118)─┬─read_from_fd.sh(8119)───sleep(8123)
└─read_from_fd.sh(8120)
答案 1 :(得分:5)
Bash 4有coprocesses。
您还可以在Bash 2,3或4中使用匿名命名管道,即process substitution。
答案 2 :(得分:3)
您可以使用nc
(又名netcat
),它允许将脚本的标准流连接到网络套接字。当然它也适用于localhost,因此您可以在脚本之间将其用于IPC。奖励是让脚本在不同的主机上运行的可能性,这对于FIFO来说是不可能的(好吧,可能在NFS上,但除非你已经安装了NFS,否则设置相当麻烦)。
答案 3 :(得分:2)
我只想指出丑陋的黑客并不希望以这种方式出生。
node -e "require('net').createServer(function(s){s.pipe(process.stdout)}).listen(1337)"
echo "write clean code they said" > /dev/tcp/localhost/1337
echo "it will pay off they said" > /dev/tcp/localhost/1337
甚至在MSysGit的Bash for Windows中工作,令我惊讶。
答案 4 :(得分:0)
您是否考虑过使用信号?如果您唯一需要的是触发一个事件(不传递参数),使用kill和trap可以完美地工作(但要注意语义,例如使用SIGUSR1)。
您可能需要重新编写逻辑,如下例所示:
subprocess_finished()
{
np=$( jobs -p | wc -l )
}
start_processing()
{
myfile="$1"
# DO SOMETHING HERE!!
kill -SIGUSR1 $2
}
CPUS=$( lscpu | grep "^CPU(s):" | rev | cut -f 1 -d ' ' | rev )
POLLPERIOD=5 # 5s between each poll
np=0
trap subprocess_finished SIGUSR1
for myfile in *
do
start_processing "$myfile" $$ &
np=$( jobs -p | wc -l )
echo "$( date +'%Y-%m-%d %H:%M:%S' ) [$!] Starting #$np on $CPUS: $myfile"
if [ $np -eq $CPUS ]
then
# Wait for one CPU to be free
trap subprocess_finished SIGUSR1
while [ $np -eq $CPUS ]
do
sleep $POLLPERIOD
done
fi
done
done
# wait for the last subprocesses
while [ ! -z "$( jobs -rp )" ]
do
sleep $POLLPERIOD
done