【问题标题】:In bash, either exit script without exiting the shell or export/set variables from within subshell在 bash 中,要么退出脚本而不退出 shell,要么从子 shell 中导出/设置变量
【发布时间】:2020-08-20 17:45:20
【问题描述】:

我有一个函数运行一组脚本,这些脚本在当前 shell 中设置变量、函数和别名。

reloadVariablesFromScript() {
  for script in "${scripts[@]}"; do
    . "$script"
  done
}

如果其中一个脚本有错误,我想退出脚本然后退出函数,而不是杀死shell。

reloadVariablesFromScript() {
  for script in "${scripts[@]}"; do
    {(
      set -e
      . "$script"
    )}
    if [[ $? -ne 0 ]]; then
      >&2 echo $script failed.  Skipping remaining scripts.
      return 1
    fi
  done
}

这会做我想要的,只是它不会在脚本中设置变量,无论脚本是成功还是失败。

没有子shell,set -e 会导致整个 shell 退出,这是不可取的。

有没有一种方法可以防止被调用的脚本在不终止 shell 的情况下继续出错,或者从子 shell 中设置/导出变量、别名和函数?

以下脚本模拟了我的问题:

test() {
  {(
    set -e

    export foo=bar
    false
    echo Should not have gotten here!
    export bar=baz
  )}

  local errorCode=$?

  echo foo="'$foo'".  It should equal 'bar'.
  echo bar="'$bar'".  It should not be set.

  if [[ $errorCode -ne 0 ]]; then
    echo Script failed correctly.  Exiting function.
    return 1
  fi

  echo Should not have gotten here!
}

test

如果最糟糕的情况变得更糟,因为这些脚本实际上并不编辑文件系统,我可以在子 shell 中运行每个脚本,检查退出代码,如果成功,则在子 shell 之外运行它。

【问题讨论】:

  • 脚本要么在当前 shell 上运行,要么不在。如果您不想退出 shell,请不要使用 set -e。
  • 只有成功的脚本中的环境变量,失败的脚本不设置它们可以吗?
  • @MarcSances 是的,这实际上是理想的。虽然,它必须包含别名和函数。
  • “必须包括别名和函数”的细节至关重要——我有一个半写的答案,它不会包括在内。函数可以导出到环境中,但别名不能;显式调用是一项重要要求。
  • @alecxs, ...如果用户没有获取脚本,他们将没有它的函数/变量/别名/等。如果成功,则在他们的父环境中,这是他们明确尝试完成的。

标签: bash shell


【解决方案1】:

请注意,set -e has a number of surprising behaviors -- 依赖它并不是普遍认为的好主意。不过需要注意的是:我们可以将环境变量、别名和 shell 函数打乱为文本:

envTest() {
  local errorCode newVars
  newVars=$(
    set -e

    {
      export foo=bar
      false
      echo Should not have gotten here!
      export bar=baz
    } >&2

    # print generate code which, when eval'd, recreates our functions and variables
    declare -p | egrep -v '^declare -[^[:space:]]*r'
    declare -f
    alias -p
  ); errorCode=$?
  if (( errorCode == 0 )); then
    eval "$newVars"
  fi
 
  printf 'foo=%q. It should equal %q\n' "$foo" "bar"
  printf 'bar=%q. It should not be set.\n' "$bar"
 
  if [[ $errorCode -ne 0 ]]; then
    echo 'Script failed correctly.  Exiting function.'
    return 1
  fi
 
  echo 'Should not have gotten here!'
}
 
envTest

请注意,如果整个脚本段成功,则此代码仅评估 either export;问题文本和 cmets 似乎表明如果不需要,这是可以接受的。

【讨论】:

  • 我遇到了一些错误 BASH_ARGC: variable may not be assigned value,BASH_ARGV、BASH_LINENO 和 BASH_SOURCE 以及 syntax error near unexpected token '(' 也是如此。我不确定哪一行导致了最后一个错误。如果重要的话,我在 Windows 中使用 Git Bash。
  • 嗯。在 bash 5.0.16 中写入这些是允许的——但您始终可以修改 egrep 以将它们取出(因为它的目的已经消除了我们从子级传递给父级的集合中的只读变量)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-23
  • 2019-06-13
相关资源
最近更新 更多