【问题标题】:Bash: Elegant way to run a command and exit if a process exited with errorBash:运行命令并在进程因错误退出时退出的优雅方式
【发布时间】:2018-10-03 13:04:16
【问题描述】:

我在 Bash 脚本中有这样的逻辑:

运行一些东西;
如果失败,请打印一些内容并停止;
如果没有,请运行其他内容。
所有这些都在ssh 会话中运行。

如果我使用$?if / else,这可能是微不足道的。 但由于脚本的可维护性,我正在寻找一些优雅的 2 行解决方案。

这是我目前所拥有的

ssh ... '
ls attributes/*'$CONF_FILE'.rb || ls -l attributes/ && exit 1;
'$EDITOR' attributes/*'$CONF_FILE'.rb '$PART_VER';'

但是,无论如何都会退出。所以我尝试了:

ssh ... '
ls attributes/*'$CONF_FILE'.rb || (ls -l attributes/ && exit 1);
'$EDITOR' attributes/*'$CONF_FILE'.rb '$PART_VER';'

但是,exit 只退出子shell。还有exiting a script from within a subshell is not elegant

有简单的两行解决方案吗?也许其他运算符优先?

【问题讨论】:

  • 简洁性和可维护性是 shell 中的对立力量。简洁的代码可能会为了简洁而牺牲正确性,从而隐藏更多的错误。遵循最佳实践的方法显然是正确的(当然,对于与相关最佳实践习语相关的人来说)并不简洁。
  • 您是否正在运行ls 只是为了查看配置文件是否已经存在?有更好的方法来做到这一点。

标签: bash operators exit logical-operators


【解决方案1】:

为清晰、正确和可维护性而编写——不是简洁:

# store your remote script as a string. Because of the quoted heredoc, no variables are
# evaluated at this time; $1, $2 and $3 are expanded only after the code is sent to the
# remote system.
script_text=$(cat <<'EOF'
  CONF_FILE=$1; PART_VER=$2; EDITOR=$3
  shopt -s nullglob                   # Return an empty list for any failed glob
  set -- attributes/*"$CONF_FILE".rb  # Replace our argument list with a glob result
  if (( $# )); then                   # Check length of that result...
    "$EDITOR" "$@" "$PART_VER"        # ...if it's nonzero, run the editor w/ that list
  else
    ls attributes                     # otherwise, run ls and fail
    exit 1
  fi
EOF
)

# generate a single string to pass to the remote shell which passes the script text
# ...and the arguments to place in $0, $1, etc while that script is running
printf -v ssh_cmd_str '%q ' \
  bash -c "$script_text" '_' "$CONF_FILE" "$PART_VER" "$EDITOR"

# ...thereafter, that command can be run as follows:
ssh -tt ... "$ssh_cmd_str"

【讨论】:

    【解决方案2】:

    不要使用子shell;使用命令组。

    ssh ... "
      ls attributes/*'$CONF_FILE'.rb || { ls -l attributes/ && exit 1; };
      '$EDITOR' attributes/*'$CONF_FILE'.rb '$PART_VER';"
    

    (注意引号的变化;这可以更好地确保本地参数扩展的结果在远程端被正确引用,尽管如果参数扩展本身包含单引号仍然会出现问题。一个适当的解决方案将运行远程端的显式shell,将本地参数作为参数,而不是使用插值来构建脚本。以下内容未经测试,但我认为我正确引用了所有内容。

    ssh ... sh -c '
              ls attributes/*"$1.rb" || { ls -l attributes/ && exit 1; };
              "$EDITOR" attributes/*"\$1.rb" "$2";
            ' _ "$CONF_FILE" "$PART_VER"
    

    )

    【讨论】:

      【解决方案3】:

      现在,我采用了复制条件,如下所示:

      ssh ... '
      ls attributes/*'$CONF_FILE'.rb >  /dev/null || ls -l --color=always attributes/
      ls attributes/*'$CONF_FILE'.rb >  /dev/null || exit 1;
      '$EDITOR' attributes/*'$CONF_FILE'.rb '$PART_VER';'
      

      (我使用&amp;&gt;- 作为将stdout 和stderr 重定向到/dev/null 的缩写,但这可能不正确,请参阅评论。)

      【讨论】:

      • &amp;&gt;- 关闭一个 FD,它不会将它定向到 /dev/null。因此,对该 FD 的所有写入总是失败。您可以测试自己 echo &gt;&amp;- 的退出状态为 1,而 echo &gt;/dev/null 成功。
      • 有意思,这样中间线总是退出?出于某种原因,目前这种方式做了它应该做的......
      • 也许您在ls 不将失败的写入视为值得非零退出状态的平台上运行?但是,不能相信它可以跨平台可靠地运行。
      猜你喜欢
      • 2021-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-14
      • 1970-01-01
      • 1970-01-01
      • 2021-03-10
      • 1970-01-01
      相关资源
      最近更新 更多