首页 > 解决方案 > 将带有参数的 bash 命令传递给函数

问题描述

我有一个非常大的 shell 脚本,它围绕特定应用程序的许多日常管理任务提供标准化的流程和逻辑包装器。

为了简化调试和维护,我尝试创建一个 RUN 函数,它可以根据标志在屏幕或日志文件输出之间切换:

##
 # Executes the command, which is passed as $1
 # If debug mode is enabled, dump full execution output to screen
 ##
function RUN()
{
  if $debug; then
    # output to screen
    $*
  else
    # Suppress screen output, but capture all output in logs
    $* &>> $logs
  fi
}

有了这样的想法,通过使用调试标志调用我的脚本,我可以获得完整的输出到屏幕:

./script -d PARAMETERS

不幸的是,我在脚本中使用这个 RUN 函数取得了非常不同的成功。我的一些功能可以正常工作,而另一些则不能。在我添加 RUN 函数包装器之前,该脚本运行良好,但我真的需要它提供的附加功能(当它工作时)。

以下是我的脚本中 RUN 函数的一些用例示例:

# Example 1, set vars for a loop
local PARAM_LIST=$(RUN "BINARY PARAM1 PARAM2 -PARAM3")
for p in ${PARAM_LIST[@]}; do
  # Stuff
done

# Example 2, basic execution with pipes
$(RUN "BINARY PARAM1 PARAM2 -PARAM3 | awk {'{ print $2,$1 }'} | xargs -n1 BINARY PARAM1 PARAM4")

# Example 3, conditional execution
for i in ${!ARRAY[@]}; do
  if [[ ! $(RUN "BINARY PARAM1 PARAM2 $i") ]]; then
    # do stuff
  fi
done

我相当确定 bash 语法是我出错的地方,但在追踪根本原因时遇到了麻烦。

谁能帮忙指出我做错了什么?

标签: bashshell

解决方案


它失败是因为您的函数需要多个命令参数(如sudo, env, xargs, find, 等),但您改为使用 shell 命令将单个字符串传递给它(如 for su, ssh, sh -c)。

您的函数需要一个 shell 命令:

function RUN()
{
  if $debug; then
    echo "Executing: $1" >&2
    # output to screen
    eval "$1"
  else
    # Suppress screen output, but capture all output in logs
    eval "$1" &>> $logs
  fi
}

您必须正确转义所有命令:

# Fails because $2 and $1 are not being escaped:
RUN "env | awk -F= '{ print $2, $1 }'"

# Succeeds because command is correctly escaped:
RUN "env | awk -F= '{ print \$2, \$1 }'"

另一个需要考虑的选项是在调试模式下编写整个脚本,然后可以选择压缩输出,这样您就不必添加任何额外的转义:

# Save stdout&stderr in case we need it
exec 5>&1 6>&2

# Optionally squash output
if ! "${debug:false}"
then
  exec &>> "$logs"
fi

# This only shows output if debug is on
BINARY PARAM1 PARAM2 -PARAM3 | awk '{ print $2,$1 }' | xargs -n1 BINARY PARAM1 PARAM4

# This always writes to stderr, whether debug is enabled or not
echo "Done" >&6

推荐阅读