庆典:我如何确保以exec使用过程中替换的终止? [英] bash: How do I ensure termination of process substitution used with exec?

查看:81
本文介绍了庆典:我如何确保以exec使用过程中替换的终止?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我运行

  $#/斌/庆典
因为我在'序列5`;做
    EXEC 3> >(SED -eS / ^ / $ I:/;回声$我......)
    回声富>及3
    回声酒吧>及3
    EXEC 3>&安培; -
DONE

那么结果是不同步的;它可能是这样的:

  1:富
1:酒吧
2:富
2:酒吧
1
3:富
3:酒吧
2-
3
4:富
5:富
4:酒吧
5:巴
4-
5-

我如何确保进程替换>?(...)在进行下一次迭代之前完成

插入睡0.1 EXEC 3 GT;&安培; - 帮助,但它是不雅的,效率低,而且不能保证始终工作。

编辑:这个例子可能看起来很傻,但它仅作说明。我在做什么是读取输入流中的一个循环,饲养每行一个过程循环中偶尔发生变化。在code更容易解释的:

 #再次,简化了插图
而IFS =读线;做
    万一$行
    @ *)
        EXEC 3>&安培; -
        文件名= $ {行:1}
        回声启动$文件名
        EXEC 3> >(排序>中$文件名;回声说完$文件名)
        ;;
    *)
        回声$行>及3
        ;;
    ESAC
DONE
EXEC 3>&安培; -


解决方案

在bash 4以下的作品,使用协进程

 #!/斌/庆典
fd_re ='^ [0-9] + $'
cleanup_and_wait(){
    如果[[$ {协处理器[1]} =〜$ fd_re]];然后
        的evalEXEC $ {协处理器[1]}&下;&放大器; -
        回声等待$ filename来完成>和2
        等待$ COPROC_PID
    科幻
}而IFS =读-r线;做
    万一$行
    @ *)
        cleanup_and_wait
        文件名= $ {行:1}
        回声启动$文件名>和2
        协处理器{排序>中$文件名;回声说完用$文件名>&放大器; 2; }
        ;;
    *)
        printf的'%s的\\ n'$行>&安培; $ {协处理器[1]}
        ;;
    ESAC
DONE
cleanup_and_wait

对于bash以前的版本,命名管道可以用来代替:

  cleanup_and_wait(){
    如果[[$ child_pid]];然后
      EXEC 4℃;&放大器; -
      回声等待$ filename来完成>和2
      等待$ child_pid
    科幻
}#这是一个有点活泼;没有强制选项mkfifo子,
然而#,比赛是不可避免的
fifo_name = $(mktemp的-u -t fifo.XXXXXX)
如果! mkfifo子$ fifo_name然后
  回声别人可能已经创建了我们的临时FIFO我们之前做过! >和2
  回声这可以表明有人企图利用竞争条件作为>和2
  回声安全vulnarability,应始终为测试。 >和2
  1号出口
科幻#确保我们清理甚至退出意外
陷阱RM -f$ fifo_name退出而IFS =读-r线;做
    万一$行
    @ *)
        cleanup_and_wait
        文件名= $ {行:1}
        回声启动$文件名>和2
        {排序>中$文件名;回声完成了$文件名>&放大器; 2; }&下;$ fifo_name与&
        child_pid = $!
        EXEC 4>中$ fifo_name
        ;;
    *)
        printf的'%s的\\ n'$行>及4
        ;;
    ESAC
DONE
cleanup_and_wait

的几个注意事项:


  • 它的安全使用的printf'%s的\\ n'$行回声$行 ;如果一行只包含 -e ,例如回声会做什么吧。
  • 使用EXIT陷阱进行清理,确保意外SIGTERM或其他错误不会离开陈旧FIFO坐在身边。

  • 如果你的平台提供了一个方法来创建一个FIFO在单个原子操作未知名称,使用它;这将避免需要我们始终测试mkfifo子是否成功的条件。

If I run

$#/bin/bash
for i in `seq 5`; do
    exec 3> >(sed -e "s/^/$i: /"; echo "$i-")
    echo foo >&3
    echo bar >&3
    exec 3>&-
done

then the result is not synchronous; it could be something like:

1: foo
1: bar
2: foo
2: bar
1-
3: foo
3: bar
2-
3-
4: foo
5: foo
4: bar
5: bar
4-
5-

How do I ensure that the process substitution >(...) is completed before proceeding to the next iteration?

Inserting sleep 0.1 after exec 3>&- helped, but it's inelegant, inefficient, and not guaranteed to always work.

EDIT: The example may look silly, but it was for illustration only. What I'm doing is reading a stream of input in a loop, feeding each line to a process which occasionally changes during the loop. Easier explained in code:

# again, simplified for illustration
while IFS= read line; do
    case $line in
    @*)
        exec 3>&-
        filename=${line:1}
        echo "starting $filename"
        exec 3> >(sort >"$filename"; echo "finished $filename")
        ;;
    *)
        echo "$line" >&3
        ;;
    esac
done
exec 3>&-

解决方案

The following works in bash 4, using coprocesses:

#!/bin/bash
fd_re='^[0-9]+$'
cleanup_and_wait() {
    if [[ ${COPROC[1]} =~ $fd_re ]] ; then
        eval "exec ${COPROC[1]}<&-"
        echo "waiting for $filename to finish" >&2
        wait $COPROC_PID
    fi
}

while IFS= read -r line; do
    case $line in
    @*)
        cleanup_and_wait
        filename=${line:1}
        echo "starting $filename" >&2
        coproc { sort >"$filename"; echo "Finished with $filename" >&2; }
        ;;
    *)
        printf '%s\n' "$line" >&${COPROC[1]}
        ;;
    esac
done
cleanup_and_wait

For prior versions of bash, a named pipe can be used instead:

cleanup_and_wait() {
    if [[ $child_pid ]] ; then
      exec 4<&-
      echo "waiting for $filename to finish" >&2
      wait $child_pid
    fi
}

# this is a bit racy; without a force option to mkfifo,
# however, the race is unavoidable
fifo_name=$(mktemp -u -t fifo.XXXXXX)
if ! mkfifo "$fifo_name" ; then
  echo "Someone else may have created our temporary FIFO before we did!" >&2
  echo "This can indicate an attempt to exploit a race condition as a" >&2
  echo "security vulnarability and should always be tested for." >&2
  exit 1
fi

# ensure that we clean up even on unexpected exits
trap 'rm -f "$fifo_name"' EXIT

while IFS= read -r line; do
    case $line in
    @*)
        cleanup_and_wait
        filename=${line:1}
        echo "starting $filename" >&2
        { sort >"$filename"; echo "finished with $filename" >&2; } <"$fifo_name" &
        child_pid=$!
        exec 4>"$fifo_name"
        ;;
    *)
        printf '%s\n' "$line" >&4
        ;;
    esac
done
cleanup_and_wait

A few notes:

  • It's safer to use printf '%s\n' "$line" than echo "$line"; if a line contains only -e, for instance, some versions of echo will do nothing with it.
  • Using an EXIT trap for cleanup ensures that an unexpected SIGTERM or other error won't leave the stale fifo sitting around.
  • If your platform provides a way to create a FIFO with an unknown name in a single, atomic operation, use it; this would avoid the condition that requires us to always test whether the mkfifo is successful.

这篇关于庆典:我如何确保以exec使用过程中替换的终止?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆