用户定义的输出重定向未按预期工作
User defined output redirection not working as expected
我正在使用 KSH
脚本来执行具有以下语法的二进制(程序)才能正确执行:
myprog [-v | --verbose (optional)] [input1] [input2]
程序不打印任何内容 & returns 成功时退出代码 0(零)。失败时它会将错误消息打印到 STDERR
& returns exit status > 0
。如果指定了 -v
选项,它会在成功和失败的情况下将详细信息打印到 STDOUT
。
为了使它可用并减少参数交换和用户控制日志记录的机会,我使用了一个 ksh shell 脚本来调用这个二进制文件。 运行 ksh shell 脚本的语法如下:
- myshell.sh [-v(可选)] [-a 输入 1] [-b 输入 2]
如果指定了 -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 $?
这里的问题是,所有输出 STDERR
和 STDOUT
都打印在屏幕上(即没有发生重定向),并且在之后没有创建 *.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 > ...
或类似操作)
我正在使用 KSH
脚本来执行具有以下语法的二进制(程序)才能正确执行:
myprog [-v | --verbose (optional)] [input1] [input2]
程序不打印任何内容 & returns 成功时退出代码 0(零)。失败时它会将错误消息打印到 STDERR
& returns exit status > 0
。如果指定了 -v
选项,它会在成功和失败的情况下将详细信息打印到 STDOUT
。
为了使它可用并减少参数交换和用户控制日志记录的机会,我使用了一个 ksh shell 脚本来调用这个二进制文件。 运行 ksh shell 脚本的语法如下:
- myshell.sh [-v(可选)] [-a 输入 1] [-b 输入 2]
如果指定了 -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 $?
这里的问题是,所有输出 STDERR
和 STDOUT
都打印在屏幕上(即没有发生重定向),并且在之后没有创建 *.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
.
你也可以更聪明一点,让 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 > ...
或类似操作)