语法错误:文件描述符 ≥10 上的 io 重定向出现意外单词

语法错误:文件描述符 ≥10 上的 io 重定向出现意外单词

我在 debian 7 上有以下 shell 脚本:

#!/bin/sh


case "$IFACE" in
    lo)
        # The loopback interface does not count.
        # only run when some other interface comes up
        exit 0
        ;;
    *)
        ;;
esac

(
flock -e 200

FLAGFILE=/var/run/cyvo-auto-started

if [ -e $FLAGFILE ]; then
    exit 0
else
    touch $FLAGFILE
fi

# Launch CyVo without the lock and as cyvo user
flock -u 200
sudo -u cyvo /usr/local/bin/cyvo-launch.sh

) 200>/var/lock/cyvo-autostart-lock # word unexpected here? (line 29)

它给我带来的错误是:

./cyvo: 29: ./cyvo: Syntax error: word unexpected

这对我来说真的很困惑,因为这是建议的语法命令。它将传递文件描述符 200/var/lock/cyvo-autostart-lock指向().

我究竟做错了什么?

答案1

您可以在 shell 脚本中移植使用的唯一文件描述符编号是 0 到 9(即单个数字)。引用POSIX:

打开的文件由从零开始的十进制数字表示。最大可能值是实现定义的;但是,所有实现都应至少支持 0 到 9(含)以供应用程序使用。这些数字称为“文件描述符”。 (…) 重定向运算符前面可以有一位或多位数字(不允许插入 <空白> 字符)来指定文件描述符编号。

语法很明显,重定向运算符之前可以有多个数字。然而,大多数 shell 仅将运算符前面的数字解析为文件描述符(如果它由单个数字组成)。例如,根据 POSIX 规范,echo 10>a应该将文件描述符 10 重定向到a并创建一个空文件,或者因为 shell 不支持 10 作为文件描述符编号而出错。错误也可能是由于文件描述符数量超过了进程打开文件数量的限制。

但实际上大多数 shell 都会将其解析为echo 10 >a,即 write 10in a。甚至 ATT ksh93 和 posh 也不合规,dash、mksh 和 zsh 也是如此:

$ ksh -c 'echo 10>a; wc -c a'
3 a
$ posh -c 'echo 10>a; wc -c a'
3 a

在 Linux 上,我只能找到符合 POSIX 标准的 bash、yash 和 BusyBox 的 ash。

$ bash -c 'echo 10>a; wc -c a' 

0 a

类似地,(…) 200>somefile应该被解析为重定向,并且可能会也可能不会工作,具体取决于 shell 是否支持这么高的文件描述符。但大多数 shell 会报告语法错误,因为它们不会将该200部分解析为文件描述符编号,而是解析为普通标记。

在相关说明中,echo 01>&1应该打印一个空行,因为01应该被解析为文件描述符编号,但同样只有 bash 和 BusyBox ash 能做到这一点。

因此,在实践中,您只能使用单位数文件描述符在 shell 脚本中。描述符0、1和2具有标准含义;描述符 3-9 可以自由使用。 Shell 不会将 fd 3–9 用于其内部目的。无论您选择什么文件描述符,当 shell 启动时文件描述符可能已经打开,但这并不重要:脚本中的任何文件描述符重新分配都保留在脚本中,不会影响脚本的父进程。

答案2

再想一想,也许比我原来的答案(我在下面复制的,因为这是最初接受的)更简单的方法是将文件描述符从200<9.例如,5。因此,将200脚本中的所有三个实例更改为5

#!/bin/sh
case "$IFACE" in
    lo)
        # The loopback interface does not count.
        # only run when some other interface comes up
        exit 0
        ;;
    *)
        ;;
esac

(
flock -e 5

FLAGFILE=/var/run/cyvo-auto-started

if [ -e $FLAGFILE ]; then
    exit 0
else
    touch $FLAGFILE
fi

# Launch CyVo without the lock and as cyvo user
flock -u 5
sudo -u cyvo /usr/local/bin/cyvo-launch.sh

) 5>/var/lock/cyvo-autostart-lock

原答案:

要么是因为sh无法按照 @GlennJackman 建议处理 >9 的文件描述符,要么是由于其他一些特殊性,您的脚本无法使用sh(基于dashDebian 的发行版)。我尝试过bash,效果很好,所以只需使用bash即可。将第一行更改为

#!/bin/sh

#!/bin/bash

然后脚本应该按预期工作。

相关内容