3

为什么我不能像在终端中那样在 bash 脚本中使用 exec 3>myfifo ?

我正在使用命名管道将 awk 过滤器变成一个简单的“服务器”,它应该能够从客户端获取文本输入,过滤它,并在 NUL 上刷新。

在终端 1 中,服务器运行如下:

$ mkfifo to_server from_server;
$ while true; do 
  # Really, this awk script BEGIN's with reading in a huge file, 
  # thus the client-server model
  awk '{sub("wrong", "correct");print;} /\0/ {fflush();}' <to_server >from_server; 
  echo "restarting..."; 
done

我有一个简单的脚本,应该将输入(以 NUL 结尾)放入输入管道,从输出管道读取,然后退出,而不向服务器发送 EOF(我们不希望服务器重新启动):

#!/bin/bash
# According to http://mywiki.wooledge.org/BashFAQ/085 , using 
# `exec 3>mypipe; echo foo >&3;` instead of `echo foo >mypipe` 
# should ensure that the pipe does not get the EOF which closes it:
exec 3>to_server;
exec 4<from_server;

cat >&3;
echo -e '\0' >&3;
while read -rd '' <&4; do echo -n "$REPLY"; break; done;

现在,如果我在终端 2 执行$ echo This is wrong | bash client.sh,我会This is correct返回,但终端 1 显示服务器重新启动!但是,如果我从终端 2 中运行来自 client.sh 的命令,它不会重新启动。

它似乎与 exec 命令有关,因为我也可以

$ exec 3>to_server; exec 4<from_server;
$ echo "This is wrong" | sh client.sh

它不会重新启动。如果我那时

$ exec 3>&-; exec 4<&-

(当然会重新启动一次)并执行

$ echo "This is wrong" | sh client.sh

它每次都会重新启动。所以脚本中的 exec 命令似乎没有效果。但是,将ls /proc/$$/fd/脚本中的 exec 命令放在后面表明它们确实指向正确的管道。

我在这里想念什么?

4

2 回答 2

3

我想我明白了!

exec 命令 确实有效,但 bash 本身在退出脚本时会关闭所有打开的文件描述符。在客户端末尾添加 sleep 5 表明服务器需要 5 秒才能最终关闭。

所以解决方案只是从其他终端打开一些文件描述符到我的命名管道,并保持它们打开,例如在终端 3:

$ exec 3>to_server; exec 4<from_server
$ # keep open for as long as server is open

或者,在服务器终端/脚本本身:

while true; do 
  # Really, this awk script BEGIN's with reading in a huge file, 
  # thus the client-server model
  awk '{sub("wrong", "correct");print;} /\0/ {fflush();}' <to_server >from_server &
  AWKPID=$!
  exec 3>to_server; exec 4<from_server
  wait "$AWKPID"
  echo "restarting..."; 
done
于 2011-03-21T11:10:50.310 回答
0

这是一个稍微不同的版本,它以五行块处理(客户端)输入。

# using gawk for Mac OS X from: http://rudix.org/packages-ghi.html#gawk

# server
rm -v to_server from_server
mkfifo to_server from_server
(
while true; do 
  (exec gawk '{sub("wrong", "correct");print;} /\0/ {fflush();}' <to_server >from_server) &
  bgpid=$!
  exec 3>to_server; exec 4<from_server
  wait "$bgpid"
  echo "restarting..." 
  done
) &


# client.sh

#!/bin/bash
exec 3>to_server
exec 4<from_server
n=0

clientserver() {
   n=0
   (
   while read -rd '' <&4; do echo -n "$REPLY"; break; done;
   IFS="" read -r -d $'\n' <&4 lines && printf 'found a trailing newline in from_server fifo \n' "$lines"
   ) &
   bgpid=$!
   printf '%s\n' "${lines[@]}" >&3
   printf '%b' '\000\n' >&3 
   wait $bgpid
   unset -v lines
   return 0
}

while IFS="" read -r -d $'\n' line; do
  n=$((n+=1))
  lines[$((n-1))]="$line"
  if [[ $n -eq 5 ]]; then
    clientserver
  fi
done

if [[ ${#lines[@]} -gt 0 ]]; then
  clientserver
fi

exit 0


# test
serverpid=$!
echo This is wrong | bash client.sh
printf '%s\n' {1..1007} | bash client.sh 
kill -TERM $serverpid
于 2011-04-01T19:32:36.187 回答