用户定义的输出重定向未按预期工作

User defined output redirection not working as expected

我正在使用 KSH 脚本来执行具有以下语法的二进制(程序)才能正确执行:

程序不打印任何内容 & returns 成功时退出代码 0(零)。失败时它会将错误消息打印到 STDERR & returns exit status > 0。如果指定了 -v 选项,它会在成功和失败的情况下将详细信息打印到 STDOUT

为了使它可用并减少参数交换和用户控制日志记录的机会,我使用了一个 ksh shell 脚本来调用这个二进制文件。 运行 ksh shell 脚本的语法如下:

如果指定了 -v 选项,ksh 将 STDOUT 重定向到 <execution_date_time>_out.log,将 STDERR 重定向到 <execution_date_time>_err.log。我的ksh脚本如下:

myshell.sh :

#! /bun/ksh

verbopt=""
log=""
arg1=""
arg2=""
dateTime=`date +%y-%m-%d_%H:%M:%S`

while getopts "va:b:" arg
do
    case $arg in

        v) # verbose output
            verbopt="-v"
            log="1>${dateTime}_out.log 2>${dateTime}_err.log"
            ;;
        a) # Input 1
            arg1=$OPTARG
            ;;
        b) # Input 2
            arg2=$OPTARG
            ;;
        *) # usage
            echo "USAGE: myshell.sh [-v] [-a input1] [-b input2]"
           exit 2
            ;;
    esac
done

if [[ -z $arg1|| -z $arg2]]
then
    echo "Missing arguments"
    exit 2
fi

myprog $verbopt $arg1 $arg2 $log
exit $?

这里的问题是,所有输出 STDERRSTDOUT 都打印在屏幕上(即没有发生重定向),并且在之后没有创建 *.log 文件执行成功或不成功(即退出状态:分别为 0 或 >0)。

谁能帮我解决这个问题? 谢谢。

问题是>没有在$log的值中展开。

恐怕您需要为此使用条件,例如:

cmd="myprog $verbopt $arg1 $arg2"

if [ "$log" ]; then
    $cmd 1>${dateTime}_out.log 2>${dateTime}_err.log
else
    $cmd
fi

看看 eval 命令。

替换...

myprog $verbopt $arg1 $arg2 $log

与:

eval myprog $verbopt $arg1 $arg2 $log

我不知道你的 myprog 是做什么的,但这是一个使用 eval 到 运行 date(有效命令)和 date xyz 的简单示例(无效命令),将输出重定向到日志。stdout/log.stderr 相应地:

$ cat logout
log='1>log.stdout 2>log.stderr'

'rm' -rf log.std* > /dev/null 2>&1

echo ""
echo 'eval date ${log}'
eval date ${log}

echo ""
echo "++++++++++++ log.stdout"
cat log.stdout
echo "++++++++++++ log.stderr"
cat log.stderr
echo "++++++++++++"

'rm' -rf log.std* > /dev/null 2>&1

echo ""
echo 'eval date xyz ${log}'
eval date xyz ${log}

echo ""
echo "++++++++++++ log.stdout"
cat log.stdout
echo "++++++++++++ log.stderr"
cat log.stderr
echo "++++++++++++"

现在运行脚本:

$ logout

eval date ${log}

++++++++++++ log.stdout
Sun Jul 23 15:56:01 CDT 2017
++++++++++++ log.stderr
++++++++++++

eval date xyz ${log}

++++++++++++ log.stdout
++++++++++++ log.stderr
date: invalid date `xyz'
++++++++++++

我会使用习语 exec redirection,运行 脚本的其余部分就好像给定的重定向在它被提供时一样运行:

if need_to_log; then
  exec >stdout_file 2>stderr_file
fi
this command will be logged if the above if statement was true

如果您之后需要恢复 stdout 和 stderr 以便脚本执行更多未记录的操作,您可以 运行 子 shell 中的日志记录部分:

(
  if need_to_log; then
    exec >stdout_file 2>stderr_file
  fi
  this command will be logged if the above if statement was true
)
this command will not be logged regardless

我还会在数组中构建命令,因此您可以向其中添加 -v 之类的内容,而不必为每个可能的参数设置单独的变量。如果将 -a-b 参数提供给 myprog 的顺序无关紧要,您可以将它们添加到数组中,而不是同时使用单独的变量。

你可以在下面看到我的版本。除了上述更改之外,如果不记录,我也不会费心获取时间戳,因为它是不需要的,并且使用内置的 ksh print.

将错误消息发送到标准错误而不是标准输出

这是我整理的:

#!/usr/bin/env ksh

# new array syntax requires ksh93+; for older ksh, use this:
# set -A cmd myprog
cmd=(myprog) # build up the command to run in an array
log_flag=0   # nonzero if the command should be logged
input_a=     # the two input filenames
input_b=

while getopts 'va:b:' arg; do
  case $arg in
    v) # verbose output
       # older ksh: set -A cmd "${cmd[@]}" -v
       cmd+=(-v)
       log_flag=1
       ;;
    a) # Input 1
       input_a=$OPTARG
       ;;
    b) # Input 2
       input_b=$OPTARG
       ;;
    *) # usage
       print -u2 "USAGE: [=12=] [-v] [-a input1] [-b input2]"
       exit 2
       ;;
  esac
done

if [[ -z $input_a || -z $input_b ]]; then
  print -u2 "[=12=]: Missing arguments"
  exit 2
fi

if (( log_flag )); then
  timestamp=$(date +%y-%m-%d_%H:%M:%S)
  exec  >"${timestamp}_out.log" 2>"${timestamp}_err.log"
fi

"${cmd[@]}" "$input_a" "$input_b"

您的时间戳使用两位数年份 (%y);那和组件之间的下划线是与 ISO 8601 标准的唯一偏差,因此我建议您继续采用标准格式。那将是 %Y-%m-%dT%H:%M:%S,或者,在具有较新版本 strftime%FT%T.

的 C 库中

你也可以更聪明一点,让 log_flag 成为空字符串或 -q,将其传递给命令,并针对空字符串测试它以确定是否不要打开日志文件,但我发现将简单的 0/1 值视为布尔值更容易遵循逻辑。

与其尝试在命令行中修改重定向,不如在解析标志时重定向流。即:

while getopts "va:b:" arg
do
    case $arg in

        v) # verbose output
            verbopt="-v"
            exec 1>${dateTime}_out.log 2>${dateTime}_err.log
            ;;
...

您需要小心一点,因为在此之后您会进行一些错误检查,并且您可能不希望以后的错误消息发送到 *_err.log,但这很容易修复。 (例如,尽快进行错误检查,或在错误检查后执行 test -n "$verbopt" && exec > ... 或类似操作)