如何控制作为参数之一传递给 bash 函数的重定向运算符?

How to control redirection operator passed as one of the arguments to a bash function?

我正在尝试在 bash 中编写一些 long/repetitive 配置和构建操作的脚本。 从显示给定命令和参数的函数开始,然后使用给定的参数执行它。 函数定义如下:

runit () {
    cmd=${@}
    echo "${cmd}"
    ${cmd}
}

runit touch /tmp/cltconf1

以上(不涉及重定向运算符)显示命令并按预期触摸目标文件。

runit echo "gEnableSecureClient=True" > clt1.conf

以上(涉及重定向操作符)执行前不显示命令,执行后clt1.conf文件内容为:

echo gEnableSecureClient=True
gEnableSecureClient=True

我可以理解重定向不受控制,因此导致 echo ${cmd} 实际上将内容 echo gEnableSecureClient=True 写入 clt1.conf,然后实际命令执行然后写入内容 gEnableSecureClient=True.

我想知道是否可以根据我的要求控制此重定向运算符。 任何 shopts 或转义序列处理都会有所帮助。

您的问题是:

How to control redirection operator passed as one of the arguments to a bash function?

发明你自己的发明并使用它来将上下文(要重定向到的文件名)传递给你的函数。您可以使用全局变量,也可以使用具有一些很少使用的参数样式的位置参数,例如 ++,例如:

runit() {
   # parse arguments - detect `++` followed by `>` and filename
   local cmd
   while (($#)); do
      case "" in
      "++") break; ;;
      *) cmd+=(""); ;;
      esac
      shift
   done
   local outf=/dev/stdout
   if (($#)) && [[ "" == "++" ]]; then
      shift
      while (($#)); do
         case "" in
         ">") outf=; shift; ;;
         *) echo "ERROR: Invalid arguments: expected >" >&2; return 1; ;;
         esac
         shift
      done
   fi
   if (($#)); then
        echo "ERROR: internal error when parsing arguments" >&2; return 1
   fi

   echo "Running ${cmd[*]} > $outf"
   "${cmd[@]}" > "$outf"
}
   
runit echo "gEnableSecureClient=True" ++ '>' clt1.conf

或者使用全局变量的例子,更简单,但更意大利面:

runit() {
   if [[ -n "${runit_outf:-}" ]]; then     
       echo "$* > $runit_outf" 
       "$@" > "$runit_outf"
       runit_outf=  # let's clear it after use
   else
       echo "$*"
       "$@"
   fi
}

# outputs to stdout
runit echo 123     

runit_outf=clt1.conf  # spaghetti code
runit echo 123        # outputs to file

这只是一个模板代码,我没有测试过,是用Whosebug写的。它不会处理文件描述符 - 为此,您可以编写自己的逻辑来解析表达式 - 即。在 >&1 中检测 &,或者编写非常不安全的代码并调用 eval.

以上介绍的方法以任何方式推荐,我永远不会写那样的代码,并且强烈反对编写如此复杂的逻辑来处理简单的情况。相反,您应该将命令的输出与命令的日志记录流区分开来,通常您会:

runit() {
    local cmd
    cmd=("$@")
    echo "${cmd[*]}" >&2
    "${cmd[@]}"
}

或根据情况在代码中使用专用的预先打开的文件描述符或将输出重定向到 /dev/tty

在继续之前,您一定要研究什么是以及何时使用 bash 数组,研究分词和文件名扩展,并使用 https://shellcheck.net 检查您的脚本。当传递带有空格和特殊字符(如 *?.

的参数时,你现在的函数将以多种方式中断