Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/shell/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linux 用户定义的输出重定向未按预期工作_Linux_Shell_Unix_Ksh - Fatal编程技术网

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

Linux 用户定义的输出重定向未按预期工作,linux,shell,unix,ksh,Linux,Shell,Unix,Ksh,我正在使用KSH脚本来执行二进制(程序),该二进制(程序)具有以下语法以正确执行: myprog[-v |--verbose(可选)][input1][input2] 程序不打印任何内容&成功时返回退出代码0(零)。失败时,它将错误消息打印到STDERR&返回exit status>0。如果指定了-v选项,则在成功和失败的情况下,都会将详细信息打印到STDOUT 为了使其可用并减少参数交换和用户控制日志记录的机会,我使用了一个kshshell脚本来调用这个二进制文件。运行ksh shell脚

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

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

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

  • myshell.sh[-v(可选)][-a输入1][-b输入2]
如果指定了
-v
选项,则ksh将
标准输出
重定向到
\u out.log
标准输出
重定向到
\u 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
都打印在屏幕上(即,没有发生重定向),也没有
*。日志文件是在成功或不成功执行后创建的(即,退出状态分别为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
与:


我不知道您的
myprog
做了什么,但这里有一个简单的示例,使用
eval
运行
date
(有效命令)和
date xyz
(无效命令),相应地将输出重定向到log.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内置的
打印
将错误消息发送到standard error而不是standard out

以下是我总结的内容:

#!/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: $0 [-v] [-a input1] [-b input2]"
       exit 2
       ;;
  esac
done

if [[ -z $input_a || -z $input_b ]]; then
  print -u2 "$0: 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
的C库中,
%FT%T


您还可以更聪明一点,将
log\u 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,但这是很容易修复的。(例如,尽早进行错误检查,或在错误检查后进行
测试-n“$verbopt”&&exec>…
或类似操作)

强烈反对使用
eval
。尤其是用户提供的字符串。这不是实现目标的安全方法。我的第一篇文章是一个如何使用
eval
的简短示例;OP仍然可以在他的脚本中使用
eval
,因为他实际上没有让用户提供任何潜在的邪恶命令;我已经更新了帖子,提供了一个例子,没有显示运行任何用户提供的字符串;现在快乐吗?:-)
while getopts "va:b:" arg
do
    case $arg in

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