微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何防止 bash 脚本中的“conda activate”调用从脚本中获取参数?

如何解决如何防止 bash 脚本中的“conda activate”调用从脚本中获取参数?

我有一个 bash 脚本,我在标志中传递多个参数(以前是位置参数,仍然有同样的问题)。在脚本中,我激活和停用不同的 conda 虚拟环境以运行不同的程序。

如果中间出现任何问题,我想为脚本添加一个选项以停止(这是一个包含许多步骤的漫长工作流程,其中一些步骤冗长和/或计算成本高)。为此,我想到在脚本的开头添加 set -e。

然而,这使得脚本在第一个激活步骤停止,因为 conda activate 命令尝试将我传递给脚本的所有参数也作为它们的参数。示例:

user@pc$ bash myscript.sh -a file1 -b file2 -c path1 -d string1
activate does not accept more than one argument:
['-a','file1','-b','file2','-c','path1','-d','string1']
user@pc$

有点不相关,请注意 conda 如何将标志和参数内容解析为空格分隔的单独参数。

在我的脚本中:

#!/bin/bash
set -e
... [stuff here,defining all the flags] ...
source /home/user/programs/miniconda3/bin/activate
... [the rest of the script]

我浏览了这里和 github,但收效甚微。我见过很多人在处理包含空格的参数和 conda 时遇到问题,但我的问题不完全是那个。我看到了一个 GitHub 问题,他们建议删除 conda activate 脚本的 @args,但是 1) 这可能会破坏事情并且 2) 我有多个环境并且跟踪每个环境的这种解决方法并不是很优化。

我的第一个问题是:能否以某种方式指定 conda activate 步骤不采用父脚本的参数?

最后,我想要做的是,如果中间出现问题,可以停止脚本。因此,我的第二个问题是:如果出现问题,是否有另一种方法可以停止脚本,例如脚本中的每个主要程序都要考虑是否继续?最佳做法是什么?

如果有什么不清楚的地方请告诉我,这是我第一次在这里发帖。

非常感谢!

解决方法

您需要清除位置参数

#!/bin/bash
set -e
... [stuff here,defining all the flags] ...

# if you need,store the current positional params
args=("$@")

# clear the params
set --

source /home/user/programs/miniconda3/bin/activate
... [the rest of the script]
,

为什么来源activate?根据我的经验,在获取文件时,您通常会定义通用函数,因此不应执行任何代码。但是,如果您希望执行代码,那么您需要了解它将像在调用脚本本身的那个点实际输入一样运行,因此如果它要处理位置参数,他们会更好处于待处理状态(即参见 glenn jackman 的回答)。

您的代码是否可以通过调用 activate 而不是源代码来工作,只传递适用于它的参数?


添加:

当我说“它将像实际输入时一样运行”时,我有点过于简单化了;我对混乱表示歉意,我应该更准确。它在当前 shell 环境的上下文中执行源文件(在您的情况下为 activate 脚本),而不是在子 shell 中执行它。不过,activate 脚本正在执行,请不要误会。

来自手册页:

从当前shell中的文件名读取并执行命令 环境并返回最后执行的命令的退出状态 从文件名。 [...] 如果提供了任何参数,它们将成为 执行文件名时的位置参数。否则 位置参数不变。

我不知道最后一部分,这可能是您需要做的事情的关键。如果以及如何在源脚本中操作位置参数,请小心,因为它可能会影响调用脚本:

</tmp/so2603> $ cat foo
#!/usr/bin/env bash

echo -n "FOO #1:"; for i; do echo -n " <$i>"; done; echo
. bar "$3"
echo -n "FOO #2:"; for i; do echo -n " <$i>"; done; echo

</tmp/so2603> $ cat bar
#!/usr/bin/env bash

echo -n "  BAR #1:"; for i; do echo -n " <$i>"; done; echo
shift
echo -n "  BAR #2 (after shift):"; for i; do echo -n " <$i>"; done; echo

if [[ -n "$BAZ" ]]; then
    set -- "baz"
    echo -n "  BAR #3 (after set):"; for i; do echo -n " <$i>"; done; echo
fi

先不设置 BAZ 运行它们,然后使用:

</tmp/so2603> $ ./foo one two "three four" five
FOO #1: <one> <two> <three four> <five>
  BAR #1: <three four>
  BAR #2 (after shift):
FOO #2: <one> <two> <three four> <five>

</tmp/so2603> $ BAZ=true ./foo one two "three four" five
FOO #1: <one> <two> <three four> <five>
  BAR #1: <three four>
  BAR #2 (after shift):
  BAR #3 (after set): <baz>
FOO #2: <baz>

但是,函数内部的位置参数与“全局”位置参数处于不同/独立的范围内,因此您可以从函数中获取并完全避免它们:

</tmp/so2603> $ cat foo
#!/usr/bin/env bash

call_bar()
{
    . bar
}

echo -n "FOO #1:"; for i; do echo -n " <$i>"; done; echo
call_bar
echo -n "FOO #2:"; for i; do echo -n " <$i>"; done; echo

bar 脚本保持不变,为您提供:

</tmp/so2603> $ ./foo one two "three four" five
FOO #1: <one> <two> <three four> <five>
  BAR #1:
  BAR #2 (after shift):
FOO #2: <one> <two> <three four> <five>

</tmp/so2603> $ BAZ=true ./foo one two "three four" five
FOO #1: <one> <two> <three four> <five>
  BAR #1:
  BAR #2 (after shift):
  BAR #3 (after set): <baz>
FOO #2: <one> <two> <three four> <five>

仅供参考,使用 set -e 有很多优点和缺点:

BashFaq - Why doesn't set -e (or set -o errexit,or trap ERR) do what I expected?

David Pashley's Writing Robust Bash Shell Scripts

当我需要严格的错误检查时,我通常更喜欢使用它,但每个脚本和程序员都是独一无二的,应该独立评估。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。