如何防止 'conda activate' 调用 bash 脚本从脚本中获取参数?
How to prevent 'conda activate' calls inside a bash script to take arguments from the script?
我有一个 bash 脚本,我在其中传递标志中的多个参数(以前是位置参数,仍然有同样的问题)。在脚本中,我为 运行 不同的程序激活和停用不同的 conda 虚拟环境。
我想添加一个选项,让脚本在中间出现任何问题时停止(这是一个包含许多步骤的漫长工作流程,其中一些步骤冗长 and/or 计算成本高)。为此,我想到了在脚本的开头添加 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 如何将标志和参数内容解析为 space 分隔的单独参数。
在脚本里面我有:
#!/bin/bash
set -e
... [stuff here, defining all the flags] ...
source /home/user/programs/miniconda3/bin/activate
... [the rest of the script]
我浏览了这里和 github,但收效甚微。我见过很多人在使用包含 space 的参数和 conda 时遇到麻烦,但我的问题不完全是那个。我看到一个 GitHub 问题,他们建议删除 conda activate 脚本的 @args,但是 1) 这可能会破坏某些东西,并且 2) 我有多个环境并且跟踪每个环境的这种解决方法并不是很优化.
我的第一个问题是:是否可以以某种方式指定 conda 激活步骤不采用父脚本的参数?
最后,我想做的是,如果中间出现问题,能够停止脚本。因此,我的第二个问题是:如果出现问题,是否还有另一种方法可以停止脚本,例如为剧本中的每一个主要节目考虑是否继续?最佳做法是什么?
第一次在这里发帖,有什么不明白的地方请告诉我。
非常感谢!
您需要清除位置参数
#!/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
?根据我的经验,当获取文件时,您通常会定义通用函数,因此不应该执行任何代码。但是,如果您希望执行代码,那么您需要了解它将 运行 就好像它实际上是在调用脚本本身的那个点输入的,所以如果它要处理位置参数,他们最好处于待处理状态(即请参阅格伦杰克曼的回答)。
您的代码是否可以仅通过调用 activate
而不是获取它并仅传递为其指定的参数来工作?
已添加:
当我说“它将 运行 就好像它是在那个时候实际输入的那样”时,我有点过于简单化了;我为混乱道歉,我应该更准确。它在当前 shell 环境的上下文中执行源文件(在您的例子中是 activate
脚本),而不是在子 shell 中执行它。不过,activate
脚本正在执行,请不要弄错。
来自手册页:
Read and execute commands from filename in the current shell
environment and return the exit status of the last command executed
from filename. [...] If any arguments are supplied, they become the
positional parameters when filename is executed. Otherwise the
positional parameters are unchanged.
我不知道最后一部分,这可能是您需要做的事情的关键。在源脚本中操作位置参数时要小心,因为它可能会影响调用脚本:
</tmp/so2603> $ cat foo
#!/usr/bin/env bash
echo -n "FOO #1:"; for i; do echo -n " <$i>"; done; echo
. bar ""
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>
不过,函数内部的位置参数在“全局”位置参数的 different/separate 范围内,因此您可以从函数中获取并完全避免使用它们:
</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
当我需要严格的错误检查时,我通常更喜欢使用它,但每个脚本和程序员都是独一无二的,应该独立评估。
我有一个 bash 脚本,我在其中传递标志中的多个参数(以前是位置参数,仍然有同样的问题)。在脚本中,我为 运行 不同的程序激活和停用不同的 conda 虚拟环境。
我想添加一个选项,让脚本在中间出现任何问题时停止(这是一个包含许多步骤的漫长工作流程,其中一些步骤冗长 and/or 计算成本高)。为此,我想到了在脚本的开头添加 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 如何将标志和参数内容解析为 space 分隔的单独参数。
在脚本里面我有:
#!/bin/bash
set -e
... [stuff here, defining all the flags] ...
source /home/user/programs/miniconda3/bin/activate
... [the rest of the script]
我浏览了这里和 github,但收效甚微。我见过很多人在使用包含 space 的参数和 conda 时遇到麻烦,但我的问题不完全是那个。我看到一个 GitHub 问题,他们建议删除 conda activate 脚本的 @args,但是 1) 这可能会破坏某些东西,并且 2) 我有多个环境并且跟踪每个环境的这种解决方法并不是很优化.
我的第一个问题是:是否可以以某种方式指定 conda 激活步骤不采用父脚本的参数?
最后,我想做的是,如果中间出现问题,能够停止脚本。因此,我的第二个问题是:如果出现问题,是否还有另一种方法可以停止脚本,例如为剧本中的每一个主要节目考虑是否继续?最佳做法是什么?
第一次在这里发帖,有什么不明白的地方请告诉我。
非常感谢!
您需要清除位置参数
#!/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
?根据我的经验,当获取文件时,您通常会定义通用函数,因此不应该执行任何代码。但是,如果您希望执行代码,那么您需要了解它将 运行 就好像它实际上是在调用脚本本身的那个点输入的,所以如果它要处理位置参数,他们最好处于待处理状态(即请参阅格伦杰克曼的回答)。
您的代码是否可以仅通过调用 activate
而不是获取它并仅传递为其指定的参数来工作?
已添加:
当我说“它将 运行 就好像它是在那个时候实际输入的那样”时,我有点过于简单化了;我为混乱道歉,我应该更准确。它在当前 shell 环境的上下文中执行源文件(在您的例子中是 activate
脚本),而不是在子 shell 中执行它。不过,activate
脚本正在执行,请不要弄错。
来自手册页:
Read and execute commands from filename in the current shell environment and return the exit status of the last command executed from filename. [...] If any arguments are supplied, they become the positional parameters when filename is executed. Otherwise the positional parameters are unchanged.
我不知道最后一部分,这可能是您需要做的事情的关键。在源脚本中操作位置参数时要小心,因为它可能会影响调用脚本:
</tmp/so2603> $ cat foo
#!/usr/bin/env bash
echo -n "FOO #1:"; for i; do echo -n " <$i>"; done; echo
. bar ""
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>
不过,函数内部的位置参数在“全局”位置参数的 different/separate 范围内,因此您可以从函数中获取并完全避免使用它们:
</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
当我需要严格的错误检查时,我通常更喜欢使用它,但每个脚本和程序员都是独一无二的,应该独立评估。