在 shell 脚本中聚集文件描述符

在 shell 脚本中聚集文件描述符

我以为这会给我不间断的begin-end配对,但事实并非如此:

#!/bin/bash
fun()(
    flock 1 || { echo >&2 "$BASHPID: FAIL: $?"; exit 1; }
    echo "$BASHPID begin"
    sleep 1;
    echo "$BASHPID end"
)
fun &
fun &
fun &
fun &
fun &
fun &
fun &
fun &
fun &
wait

我究竟做错了什么?

答案1

这种方法有效:

fun()(
  (flock 9 || { echo >&2 "$BASHPID: FAIL: $?"; exit 1; }
   echo "$BASHPID begin"
   sleep 1;
   echo "$BASHPID end"
  ) 9>test
)

这可以确保只要需要保护的命令尚未完成,锁定的文件就不会关闭。 (显然你应该更换test更合适的东西,例如使用mktemp。)

答案2

失败的原因可以在man 2 flock:

由flock()创建的锁与打开的文件描述相关联(参见open(2))。这意味着重复的文件描述符(例如,由 fork(2) 或 dup(2) 创建的)引用相同的锁,并且可以使用这些描述符中的任何一个来修改或释放该锁。

这意味着,由于所有进程都继承相同的文件描述符,因此当其中一个进程进行锁定时,它们都会共享该文件描述符。两次锁定同一个文件描述符是无操作的。

我通常的解决方案是锁定脚本本身(尽管如果您同时运行脚本多次,这确实会带来问题)。

#!/bin/bash
fun()(
    exec 3<"$0"
    flock 3 || { echo >&2 "$BASHPID: FAIL: $?"; exit 1; }
    echo "$BASHPID begin"
    sleep 1;
    echo "$BASHPID end"
)
fun &
fun &
fun &
fun &
fun &
fun &
fun &
fun &
fun &
wait

答案3

我认为重点是:

  • file descriptor1 保留用于stdout并已使用。我们应该使用 stdin/out/err 之外的其他内容,例如 9人 1 群示例显示。

  • flock with file descriptor需要使用指定的文件描述符编号打开锁定文件,如下所示人 1 群示例如下:

    (
      flock -n 9 || exit 1
      # ... commands executed under lock ...
    ) 9>/var/lock/mylockfile
    

结果,答案是@Stephen Kitt 的。

相关内容