脚本一直有效,直到它被修改为重定向stdout& stderr到日志文件

时间:2013-07-19 20:13:43

标签: bash file-descriptor

说明

下面的脚本运行时没有修改,它输出(正确):

  

两个START

在下面的脚本中取消注释exec语句后,文件描述符3指向标准输出,文件描述符4指向标准错误:

exec 3>&1 4>&2

所有标准输出和&标准错误记录到文件而不是打印到控制台:

# Two variances seen in the script below
exec 1>"TEST-${mode:1}.log" 2>&1  # Log Name based on subcommand
exec 1>"TEST-bootstrap.log" 2>&1  # Initial "bootstrap" log file

当exec语句到位时,脚本应该创建三个文件(包含内容):

TEST-bootstrap.log(成功创建 - 空文件)

  

TEST-one.log(成功创建)

  

一个START

TEST-two.log(未创建)

  

两个START

但它似乎在第一个日志文件之后停止,并且从不创建TEST-two.log。考虑到它与注释掉的exec语句有关,可能会出现什么问题?

剧本

#!/bin/bash
SELFD=$(cd -P $(dirname ${0}) >/dev/null 2>&1; pwd)  # Current scripts directory
SELF=$(basename ${0})   # Current scripts name
SELFX=${SELFD}/${SELF}  # The current script exec path

fnOne() { echo "one ${*}"; }
fnTwo() { echo "two ${*}"; }

subcommand() {
    # exec 3>&1 4>&2
    # exec 1>"TEST-${mode:1}.log" 2>&1
    case "${mode}" in
        -one) fnOne   ${*}; ;;
        -two) fnTwo   ${*}; ;;
    esac
}

bootstrap() {
    # exec 3>&1 4>&2
    # exec 1>"TEST-bootstrap.log" 2>&1
    echo "START" \
        | while read line; do ${SELFX} -one ${line}; done \
        | while read line; do ${SELFX} -two ${line}; done
    exit 0
}

mode=${1}; shift

case "${mode:0:1}" in
    -) subcommand ${*} ;;
    *) bootstrap ${mode} ${*} ;;
esac
exit 0

这个脚本严格地说是我在另一个更复杂的脚本中面临的问题的一个孤立的例子。我试图尽可能地保持简洁,但如果需要,我会对其进行扩展。

我想要完成的事情(为那些感兴趣的人额外阅读)

在上面的 The Script 中,为了简单起见,我使用的是while循环而不是,并使用简单的函数回显“one”和“two”以便于调试和放大;在这里提问。

在我的实际脚本中,该程序将具有以下功能:list-archivesdownloadextractprocess,用于从URL获取存档列表,下载它们,提取它们的内容,并分别处理生成的文件。考虑到这些操作需要不同的时间,我计划并行运行它们。我的脚本的bootstrap()函数看起来像这样:

# program ./myscript
list-archives "${1}" \
    | parallel myscript -download \
    | parallel myscript -extract \
    | parallel myscript -process

会像这样调用:

./myscript "http://www.example.com"

我想要完成的是一种启动程序的方法,该程序可以并行调用它自己的函数并记录它所做的一切。这对于确定上次提取数据的时间,调试任何错误等非常有用。

我还希望在使用子命令调用程序时记录日志,例如

# only list achives
>> ./myscript -list-archives "http://www.example.com"
# download this specific archive
>> ./myscript -download "http://www.example.com/archive.zip" 
# ...

2 个答案:

答案 0 :(得分:3)

我认为这是问题所在:

echo "START" \
    | while read line; do ${SELFX} -one ${line}; done \
    | while read line; do ${SELFX} -two ${line}; done

第一个while循环从START语句中读取echo。第二个while循环处理第一个while循环的输出。但是由于脚本将stdout重定向到文件,因此没有任何内容通过管道连接到第二个循环,因此它会立即退出。

我不确定如何“修复”这个,因为你不清楚你想要完成什么。如果你想为管道中的下一个命令提供东西,你可以回显到FD 3,你已经小心翼翼地用它来保存原始的标准输出。

答案 1 :(得分:0)

我已接受@Barmar's answer作为答案,@Wumpus Q. Wumbley留下的评论将我推向了正确的方向。

解决方案是在case语句中使用tee /dev/fd/3,如下所示:

subcommand() {
    exec 3>&1 4>&2
    exec 1>"TEST-${mode:1}.log" 2>&1
    case "${mode}" in
        -one) fnOne   ${*}; ;;
        -two) fnTwo   ${*}; ;;
    esac | tee /dev/fd/3
}