一篇文章搞定ROS2的setup.bash
关于ROS2的setup.bash的详解
·
引入
- 相信学过ROS2的各位都十分熟悉一下的这条指令,在每次执行一些ROS2操作的时候都需要先执行这条刷新环境变量的脚本,那么这个脚本究竟做了什么,今天我们来看一看。
source ./install/setup.bash
Shell
- Bash是Shell的一种,Shell是操作系统与用户之间的接口,允许用户通过命令行输入命令来控制计算机。Shell不仅限于Bash,还有其他多种类型,每种Shell都有其特点和功能。
Shell的种类:
- Bash (Bourne-Again SHell):是Bourne Shell(sh)的增强版本,是最流行的Linux Shell之一。它是一个命令行解释器,用户可以通过它输入命令,并与操作系统交互。
- Bourne Shell (sh):是原始的Unix Shell,由Steve Bourne在贝尔实验室开发。
- C Shell (csh):提供类似C语言的语法,适合编程。
- Korn Shell (ksh):结合了Bourne Shell和C Shell的特性,提供高级编程功能。
- Z Shell (zsh):是一个功能丰富的Shell,提供了强大的命令行编辑、补全和脚本编程能力。
Bash与Shell的关系:
- Bash是Shell的一种实现,它遵循Shell的基本原则和功能,同时提供了额外的特性和改进。
- Bash是大多数Linux发行版的默认Shell,而其他Shell可能在不同的系统或环境中更受欢迎。
- Bash与其他Shell相比,提供了更强大的脚本编程能力和更友好的用户界面。
- Bash兼容Bourne Shell的脚本,这意味着大多数为Bourne Shell编写的脚本可以在Bash中运行,而无需修改。
总的来说,Bash是Shell的一种,但Shell的概念更广泛,包括了多种不同的实现。每种Shell都有其特定的用途和优势,用户可以根据自己的需求和偏好选择合适的Shell。
setup.bash
- 底下就是完整的setup.bash脚本,接下来我们将逐行分析,先来看看这个脚本到底做了什么
- 环境扩展:脚本的主要功能是扩展当前的环境设置。在Linux和Unix系统中,环境通常包括一系列变量,如
PATH
、LD_LIBRARY_PATH
等,它们决定了系统如何解释和执行用户的命令。 - 其他前缀路径:脚本会包括(或“source”)在生成这个脚本时已经设置的其他路径。这意味着,如果之前有其他的脚本或设置已经被加载到环境中,这个脚本会确保这些设置被保留和继承。
- 包含的功能包:脚本不仅包括路径设置,还包括
所有
在这个路径下的功能包。
- 环境扩展:脚本的主要功能是扩展当前的环境设置。在Linux和Unix系统中,环境通常包括一系列变量,如
# generated from colcon_bash/shell/template/prefix_chain.bash.em
# This script extends the environment with the environment of other prefix
# paths which were sourced when this file was generated as well as all packages
# contained in this prefix path.
# function to source another script with conditional trace output
# first argument: the path of the script
_colcon_prefix_chain_bash_source_script() {
if [ -f "$1" ]; then
if [ -n "$COLCON_TRACE" ]; then
echo "# . \"$1\""
fi
. "$1"
else
echo "not found: \"$1\"" 1>&2
fi
}
# source chained prefixes
# setting COLCON_CURRENT_PREFIX avoids determining the prefix in the sourced script
COLCON_CURRENT_PREFIX="/opt/tros"
_colcon_prefix_chain_bash_source_script "$COLCON_CURRENT_PREFIX/local_setup.bash"
# source this prefix
# setting COLCON_CURRENT_PREFIX avoids determining the prefix in the sourced script
COLCON_CURRENT_PREFIX="$(builtin cd "`dirname "${BASH_SOURCE[0]}"`" > /dev/null && pwd)"
_colcon_prefix_chain_bash_source_script "$COLCON_CURRENT_PREFIX/local_setup.bash"
unset COLCON_CURRENT_PREFIX
unset _colcon_prefix_chain_bash_source_script
# generated from colcon_bash/shell/template/prefix_chain.bash.em
:解释了这个脚本是由colcon工具自动生成的(每次执行colcon build会在install下自动生成该脚本)- 再来看下面这个函数
_colcon_prefix_chain_bash_source_script() {
if [ -f "$1" ]; then
if [ -n "$COLCON_TRACE" ]; then
echo "# . \"$1\""
fi
. "$1"
else
echo "not found: \"$1\"" 1>&2
fi
}
- 这个函数的作用是source另一个脚本,同时判断了一些输出跟踪信息
# first argument: the path of the script
函数的注释指出该函数的第一个参数应该为需要source的脚本的路径if [ -f "$1" ];
检查第一参数是否存在,也就是需要source的脚本文件是否存在if [ -n "$COLCON_TRACE" ];
检查环境变量COLCON_TRACE
是否被设置。-n
是一个测试操作符,用于检查字符串是否非空。COLCON_TRACE
是一个环境变量,用于colcon
,它指示colcon
或相关的脚本输出额外的信息,这些信息有助于调试和理解colcon
的行为。- 通常可以这样设置
COLCON_TRACE=1 colcon build
echo "# . \"$1\""
:输出一个注释-
echo
:是Bash的内置命令,用于输出文本。
- 在Bash中,以
#
开头的行被视为注释 - 连起来:
.
表示source命令,"$1"
是函数的第一个参数,即要source的脚本路径。
-
fi
:结束if判断. "$1"
:不用多说了,source该脚本路径echo "not found: \"$1\"" 1>&2
:如果找不到该文件,那就输出脚本找不到,1>&2
表示将输出重定向到标准错误(stderr)。
- 然后来看这个函数的调用,source了/opt/tros/local_setup.bash
- 备注这里的tros是由于是由地平线公司的ros改版,正常应该是ros
# source chained prefixes
# setting COLCON_CURRENT_PREFIX avoids determining the prefix in the sourced script
COLCON_CURRENT_PREFIX="/opt/tros"
_colcon_prefix_chain_bash_source_script "$COLCON_CURRENT_PREFIX/local_setup.bash"
- 再来看看,设置当前脚本的目录作为环境变量
COLCON_CURRENT_PREFIX
的值,并source该目录下的local_setup.bash
脚本
# source this prefix
# setting COLCON_CURRENT_PREFIX avoids determining the prefix in the sourced script
COLCON_CURRENT_PREFIX="$(builtin cd "`dirname "${BASH_SOURCE[0]}"`" > /dev/null && pwd)"
_colcon_prefix_chain_bash_source_script "$COLCON_CURRENT_PREFIX/local_setup.bash"
"$(builtin cd "
dirname “${[0]}”" > /dev/null && pwd)"
详细看看这一行贼长的东东builtin cd
:这是一个Bash内置命令,用于改变当前工作目录。这里使用builtin
是为了确保使用的是Bash的内置版本,而不是可能已被用户定义的同名函数。dirname "BASH_SOURCE[0]"‘"‘:
是Bash的特殊变量,表示当前脚本的文件名。dirname
命令用于从文件名中提取目录路径。> /dev/null
将cd
命令的输出重定向到/dev/null
,即丢弃输出。这样做的目的是避免在终端上显示cd
命令的输出。&& pwd
:同时执行pwd
命令,以获取当前工作目录的绝对路径。
- 那么最后两行,删除之前设置的环境变量,避免反复使用环境变量冲突或者混淆
unset COLCON_CURRENT_PREFIX
unset _colcon_prefix_chain_bash_source_script
小结
- 是不是看了上述代码,发现疑点没少反而变多了?那么接下来看看这两个脚本做了什么
- /opt/tros/local_setup.bash
- /local_setup.bash
/opt/tros/local_setup.bash
- 哈哈哈哈是不是很长?我们慢慢看
# Determine whether the login account is root, if not, switch to the root account
if [ "$(id -u)" -ne 0 ]; then
echo "Please use the 'root' account to log in (default password is 'root'), and re-run 'source /opt/tros/setup.bash'!" >&2
su
fi
# generated from colcon_bash/shell/template/prefix.bash.em
# This script extends the environment with all packages contained in this
# prefix path.
# a bash script is able to determine its own path if necessary
if [ -z "$COLCON_CURRENT_PREFIX" ]; then
_colcon_prefix_bash_COLCON_CURRENT_PREFIX="$(builtin cd "`dirname "${BASH_SOURCE[0]}"`" > /dev/null && pwd)"
else
_colcon_prefix_bash_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX"
fi
# function to prepend a value to a variable
# which uses colons as separators
# duplicates as well as trailing separators are avoided
# first argument: the name of the result variable
# second argument: the value to be prepended
_colcon_prefix_bash_prepend_unique_value() {
# arguments
_listname="$1"
_value="$2"
# get values from variable
eval _values=\"\$$_listname\"
# backup the field separator
_colcon_prefix_bash_prepend_unique_value_IFS="$IFS"
IFS=":"
# start with the new value
_all_values="$_value"
_contained_value=""
# iterate over existing values in the variable
for _item in $_values; do
# ignore empty strings
if [ -z "$_item" ]; then
continue
fi
# ignore duplicates of _value
if [ "$_item" = "$_value" ]; then
_contained_value=1
continue
fi
# keep non-duplicate values
_all_values="$_all_values:$_item"
done
unset _item
if [ -z "$_contained_value" ]; then
if [ -n "$COLCON_TRACE" ]; then
if [ "$_all_values" = "$_value" ]; then
echo "export $_listname=$_value"
else
echo "export $_listname=$_value:\$$_listname"
fi
fi
fi
unset _contained_value
# restore the field separator
IFS="$_colcon_prefix_bash_prepend_unique_value_IFS"
unset _colcon_prefix_bash_prepend_unique_value_IFS
# export the updated variable
eval export $_listname=\"$_all_values\"
unset _all_values
unset _values
unset _value
unset _listname
}
# add this prefix to the COLCON_PREFIX_PATH
_colcon_prefix_bash_prepend_unique_value COLCON_PREFIX_PATH "$_colcon_prefix_bash_COLCON_CURRENT_PREFIX"
unset _colcon_prefix_bash_prepend_unique_value
# check environment variable for custom Python executable
if [ -n "$COLCON_PYTHON_EXECUTABLE" ]; then
if [ ! -f "$COLCON_PYTHON_EXECUTABLE" ]; then
echo "error: COLCON_PYTHON_EXECUTABLE '$COLCON_PYTHON_EXECUTABLE' doesn't exist"
return 1
fi
_colcon_python_executable="$COLCON_PYTHON_EXECUTABLE"
else
# try the Python executable known at configure time
_colcon_python_executable="/usr/bin/python3"
# if it doesn't exist try a fall back
if [ ! -f "$_colcon_python_executable" ]; then
if ! /usr/bin/env python3 --version > /dev/null 2> /dev/null; then
echo "error: unable to find python3 executable"
return 1
fi
_colcon_python_executable=`/usr/bin/env python3 -c "import sys; print(sys.executable)"`
fi
fi
# function to source another script with conditional trace output
# first argument: the path of the script
_colcon_prefix_sh_source_script() {
if [ -f "$1" ]; then
if [ -n "$COLCON_TRACE" ]; then
echo "# . \"$1\""
fi
. "$1"
else
echo "not found: \"$1\"" 1>&2
fi
}
# get all commands in topological order
_colcon_ordered_commands="$($_colcon_python_executable "$_colcon_prefix_bash_COLCON_CURRENT_PREFIX/_local_setup_util_sh.py" sh bash --merged-install)"
unset _colcon_python_executable
if [ -n "$COLCON_TRACE" ]; then
echo "$(declare -f _colcon_prefix_sh_source_script)"
echo "# Execute generated script:"
echo "# <<<"
echo "${_colcon_ordered_commands}"
echo "# >>>"
echo "unset _colcon_prefix_sh_source_script"
fi
eval "${_colcon_ordered_commands}"
unset _colcon_ordered_commands
unset _colcon_prefix_sh_source_script
unset _colcon_prefix_bash_COLCON_CURRENT_PREFIX
export TROS_DISTRO=
- 检测当前登录的用户是否是root用户,确保只有root用户才能运行后续的命令
- 所以这也是ROS2运行时一般使用root用户的原因~
"$(id -u)" -ne 0:
id -u
命令来获取当前用户的用户ID。在Linux中,root用户的用户ID为0。这里检测是否不是root用户,如果不是,那就输出并尝试切换为root账户
# Determine whether the login account is root, if not, switch to the root account
if [ "$(id -u)" -ne 0 ]; then
echo "Please use the 'root' account to log in (default password is 'root'), and re-run 'source /opt/tros/setup.bash'!" >&2
su
fi
- 下面代码用户配置
_colcon_prefix_bash_COLCON_CURRENT_PREFIX
,这个变量用于存储当前脚本所在目录的路径- 这里判断这个变量是否被设置,确定当前脚本的路径
# generated from colcon_bash/shell/template/prefix.bash.em
# This script extends the environment with all packages contained in this
# prefix path.
# a bash script is able to determine its own path if necessary
if [ -z "$COLCON_CURRENT_PREFIX" ]; then
_colcon_prefix_bash_COLCON_CURRENT_PREFIX="$(builtin cd "`dirname "${BASH_SOURCE[0]}"`" > /dev/null && pwd)"
else
_colcon_prefix_bash_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX"
fi
_colcon_prefix_bash_prepend_unique_value函数
- 紧接着来看看这个长函数
# function to prepend a value to a variable
# which uses colons as separators
# duplicates as well as trailing separators are avoided
# first argument: the name of the result variable
# second argument: the value to be prepended
_colcon_prefix_bash_prepend_unique_value() {
# arguments
_listname="$1"
_value="$2"
# get values from variable
eval _values=\"\$$_listname\"
# backup the field separator
_colcon_prefix_bash_prepend_unique_value_IFS="$IFS"
IFS=":"
# start with the new value
_all_values="$_value"
_contained_value=""
# iterate over existing values in the variable
for _item in $_values; do
# ignore empty strings
if [ -z "$_item" ]; then
continue
fi
# ignore duplicates of _value
if [ "$_item" = "$_value" ]; then
_contained_value=1
continue
fi
# keep non-duplicate values
_all_values="$_all_values:$_item"
done
unset _item
if [ -z "$_contained_value" ]; then
if [ -n "$COLCON_TRACE" ]; then
if [ "$_all_values" = "$_value" ]; then
echo "export $_listname=$_value"
else
echo "export $_listname=$_value:\$$_listname"
fi
fi
fi
unset _contained_value
# restore the field separator
IFS="$_colcon_prefix_bash_prepend_unique_value_IFS"
unset _colcon_prefix_bash_prepend_unique_value_IFS
# export the updated variable
eval export $_listname=\"$_all_values\"
unset _all_values
unset _values
unset _value
unset _listname
}
- 这个函数看似很长其实时用于将一个值添加到使用冒号分隔的变量中,同时避免重复和尾随分隔符。
- 说人话:假设我有个路径如下,然后我想添加一个新的路径
/home/user/bin
到PATH
中,但是要求这个新的路径不能已经存在于PATH
中,也不能是PATH
中的最后一个元素。那么我就调用这个函数_colcon_prefix_bash_prepend_unique_value
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- 那么我就可以这样使用:
set_path() { _listname="PATH" _value="/home/user/bin" _colcon_prefix_bash_prepend_unique_value "$_listname" "$_value" }
- 说人话:假设我有个路径如下,然后我想添加一个新的路径
_listname="$1"
:将函数的第一个参数赋值给变量_listname
,此参数是结果变量的名称。_value="$2"
:将函数的第二个参数赋值给变量_value
,此个参数是要添加的值。eval _values=\"\$$_listname\"
:这行代码使用eval
命令计算并赋值给变量_values
。\$$_listname
表示对变量_listname
的值进行变量替换。eval
命令会将参数作为命令来执行,参数可以包括变量、表达式或其他命令。变量替换
是Bash中的一个概念,它允许你将变量的值替换为变量名本身
_colcon_prefix_bash_prepend_unique_value_IFS="$IFS"
:备份当前的域分隔符IFS
,最后可以恢复IFS=":"
:这行代码将域分隔符IFS
设置为冒号:
,规定路径用分隔符分开- 两行初始化变量
- 然后看看这个for–确保了
_all_values
变量只包含非重复的值-
for _item in $_values; do
for循环,_item
表示每次循环从$_values
拿出一个变量,$_values
是需要添加的变量(此函数的第二个参数)
if [ -z "$_item" ]; then
:这行代码检查当前值$_item
是否为空,如果空就跳if [ "$_item" = "$_value" ]; then
:检查新添加的这个路径是否已经存在了
-
# iterate over existing values in the variable
for _item in $_values; do
# ignore empty strings
if [ -z "$_item" ]; then
continue
fi
# ignore duplicates of _value
if [ "$_item" = "$_value" ]; then
_contained_value=1
continue
fi
# keep non-duplicate values
_all_values="$_all_values:$_item"
done
- 然后是这个函数后续部分
unset _item
if [ -z "$_contained_value" ]; then
if [ -n "$COLCON_TRACE" ]; then
if [ "$_all_values" = "$_value" ]; then
echo "export $_listname=$_value"
else
echo "export $_listname=$_value:\$$_listname"
fi
fi
fi
unset _contained_value
# restore the field separator
IFS="$_colcon_prefix_bash_prepend_unique_value_IFS"
unset _colcon_prefix_bash_prepend_unique_value_IFS
# export the updated variable
eval export $_listname=\"$_all_values\"
unset _all_values
unset _values
unset _value
unset _listname
unset _item
:这行代码删除变量_item
,循环完就没用咯- 然后是一系列调试信息的判断,有些已经讲过,这里懒的说了(打字打累了思密达)
echo "export $_listname=$_value"
export
命令是 Bash 中的一个内置命令,用于设置或显示环境变量。
- 然后是两行恢复分隔符
eval export $_listname=\"$_all_values\"
:用于将一个变量的值设置为另一个变量的值eval
指令是 Bash 中的一个特殊命令,用于计算并执行它的一个或多个参数,这里进行参数替换
- 最后恢复一些设置的变量
- 加油你已经看一半了…接下来是这个函数的调用
# add this prefix to the COLCON_PREFIX_PATH
_colcon_prefix_bash_prepend_unique_value COLCON_PREFIX_PATH "$_colcon_prefix_bash_COLCON_CURRENT_PREFIX"
unset _colcon_prefix_bash_prepend_unique_value
- 将当前脚本所在目录的路径添加到环境变量
COLCON_PREFIX_PATH
中。
- 接下来将检查环境变量
COLCON_PYTHON_EXECUTABLE
。COLCON_PYTHON_EXECUTABLE
这个环境变量指定了一个自定义的 Python 解释器路径
# check environment variable for custom Python executable
if [ -n "$COLCON_PYTHON_EXECUTABLE" ]; then
if [ ! -f "$COLCON_PYTHON_EXECUTABLE" ]; then
echo "error: COLCON_PYTHON_EXECUTABLE '$COLCON_PYTHON_EXECUTABLE' doesn't exist"
return 1
fi
_colcon_python_executable="$COLCON_PYTHON_EXECUTABLE"
else
# try the Python executable known at configure time
_colcon_python_executable="/usr/bin/python3"
# if it doesn't exist try a fall back
if [ ! -f "$_colcon_python_executable" ]; then
if ! /usr/bin/env python3 --version > /dev/null 2> /dev/null; then
echo "error: unable to find python3 executable"
return 1
fi
_colcon_python_executable=`/usr/bin/env python3 -c "import sys; print(sys.executable)"`
fi
fi
- 上述代码逻辑很简单就是判断是否指定了解释器路径,没有就使用默认的`/usr/bin/python3
if ! /usr/bin/env python3 --version > /dev/null 2> /dev/null; then
检测python3是否存在/usr/bin/env python3 -c "import sys; print(sys.executable)"
这里大家肯定不陌生,使用env
命令和Python的sys.executable
属性来获取Python解释器的路径。`
import sys
print(sys.executable)
- 倒数第二个,来看看这个函数,用于source(执行)另一个脚本,并在特定条件下输出调试信息。这个函数和setup.bash的
_colcon_prefix_chain_bash_source_script
差不多,这里不说了
# function to source another script with conditional trace output
# first argument: the path of the script
_colcon_prefix_sh_source_script() {
if [ -f "$1" ]; then
if [ -n "$COLCON_TRACE" ]; then
echo "# . \"$1\""
fi
. "$1"
else
echo "not found: \"$1\"" 1>&2
fi
}
- 来看看最后的部分,获取所有命令,并执行
# get all commands in topological order
_colcon_ordered_commands="$($_colcon_python_executable "$_colcon_prefix_bash_COLCON_CURRENT_PREFIX/_local_setup_util_sh.py" sh bash --merged-install)"
unset _colcon_python_executable
if [ -n "$COLCON_TRACE" ]; then
echo "$(declare -f _colcon_prefix_sh_source_script)"
echo "# Execute generated script:"
echo "# <<<"
echo "${_colcon_ordered_commands}"
echo "# >>>"
echo "unset _colcon_prefix_sh_source_script"
fi
eval "${_colcon_ordered_commands}"
unset _colcon_ordered_commands
unset _colcon_prefix_sh_source_script
unset _colcon_prefix_bash_COLCON_CURRENT_PREFIX
export TROS_DISTRO=
_colcon_ordered_commands="$($_colcon_python_executable "$_colcon_prefix_bash_COLCON_CURRENT_PREFIX/_local_setup_util_sh.py" sh bash --merged-install)"
这一行调用Python脚本_local_setup_util_sh.py
(这个下一期再讲吧),该脚本同样位于当前脚本所在目录下的_colcon_prefix_bash_COLCON_CURRENT_PREFIX
目录中。echo "unset _colcon_prefix_sh_source_script"
使用eval
命令执行_colcon_ordered_commands
变量中包含的所有命令。这些命令是按照拓扑顺序排列的,确保它们能够正确地执行。
小结
- 以上就是
/opt/tros/local_setup.bash
的完整说明,简单总结就是确保了正确的环境配置,以便能够顺利地使用和构建ROS 2软件包。
- 检查当前用户权限:脚本首先检查当前登录的用户是否是root用户。如果不是,脚本会提示用户使用root账户登录,并重新运行
source /opt/tros/setup.bash
命令。 - 设置环境变量
COLCON_CURRENT_PREFIX
:脚本确定当前脚本的目录路径,并将其存储在环境变量COLCON_CURRENT_PREFIX
中。 - 处理环境变量
COLCON_PYTHON_EXECUTABLE
:脚本检查是否存在自定义的Python解释器路径。如果存在,脚本使用该路径;否则,尝试使用系统默认的Python解释器。 - 执行拓扑排序的命令:脚本使用Python脚本
_local_setup_util_sh.py
来生成和排序所有命令,然后使用eval
命令按拓扑顺序执行这些命令。
/local_setup.bash
- 再来看看这个脚本
# generated from colcon_core/shell/template/prefix.sh.em
# This script extends the environment with all packages contained in this
# prefix path.
# since a plain shell script can't determine its own path when being sourced
# either use the provided COLCON_CURRENT_PREFIX
# or fall back to the build time prefix (if it exists)
_colcon_prefix_sh_COLCON_CURRENT_PREFIX="/root/dev_ws/install"
if [ -z "$COLCON_CURRENT_PREFIX" ]; then
if [ ! -d "$_colcon_prefix_sh_COLCON_CURRENT_PREFIX" ]; then
echo "The build time path \"$_colcon_prefix_sh_COLCON_CURRENT_PREFIX\" doesn't exist. Either source a script for a different shell or set the environment variable \"COLCON_CURRENT_PREFIX\" explicitly." 1>&2
unset _colcon_prefix_sh_COLCON_CURRENT_PREFIX
return 1
fi
else
_colcon_prefix_sh_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX"
fi
# function to prepend a value to a variable
# which uses colons as separators
# duplicates as well as trailing separators are avoided
# first argument: the name of the result variable
# second argument: the value to be prepended
_colcon_prefix_sh_prepend_unique_value() {
# arguments
_listname="$1"
_value="$2"
# get values from variable
eval _values=\"\$$_listname\"
# backup the field separator
_colcon_prefix_sh_prepend_unique_value_IFS="$IFS"
IFS=":"
# start with the new value
_all_values="$_value"
_contained_value=""
# iterate over existing values in the variable
for _item in $_values; do
# ignore empty strings
if [ -z "$_item" ]; then
continue
fi
# ignore duplicates of _value
if [ "$_item" = "$_value" ]; then
_contained_value=1
continue
fi
# keep non-duplicate values
_all_values="$_all_values:$_item"
done
unset _item
if [ -z "$_contained_value" ]; then
if [ -n "$COLCON_TRACE" ]; then
if [ "$_all_values" = "$_value" ]; then
echo "export $_listname=$_value"
else
echo "export $_listname=$_value:\$$_listname"
fi
fi
fi
unset _contained_value
# restore the field separator
IFS="$_colcon_prefix_sh_prepend_unique_value_IFS"
unset _colcon_prefix_sh_prepend_unique_value_IFS
# export the updated variable
eval export $_listname=\"$_all_values\"
unset _all_values
unset _values
unset _value
unset _listname
}
# add this prefix to the COLCON_PREFIX_PATH
_colcon_prefix_sh_prepend_unique_value COLCON_PREFIX_PATH "$_colcon_prefix_sh_COLCON_CURRENT_PREFIX"
unset _colcon_prefix_sh_prepend_unique_value
# check environment variable for custom Python executable
if [ -n "$COLCON_PYTHON_EXECUTABLE" ]; then
if [ ! -f "$COLCON_PYTHON_EXECUTABLE" ]; then
echo "error: COLCON_PYTHON_EXECUTABLE '$COLCON_PYTHON_EXECUTABLE' doesn't exist"
return 1
fi
_colcon_python_executable="$COLCON_PYTHON_EXECUTABLE"
else
# try the Python executable known at configure time
_colcon_python_executable="/usr/bin/python3"
# if it doesn't exist try a fall back
if [ ! -f "$_colcon_python_executable" ]; then
if ! /usr/bin/env python3 --version > /dev/null 2> /dev/null; then
echo "error: unable to find python3 executable"
return 1
fi
_colcon_python_executable=`/usr/bin/env python3 -c "import sys; print(sys.executable)"`
fi
fi
# function to source another script with conditional trace output
# first argument: the path of the script
_colcon_prefix_sh_source_script() {
if [ -f "$1" ]; then
if [ -n "$COLCON_TRACE" ]; then
echo "# . \"$1\""
fi
. "$1"
else
echo "not found: \"$1\"" 1>&2
fi
}
# get all commands in topological order
_colcon_ordered_commands="$($_colcon_python_executable "$_colcon_prefix_sh_COLCON_CURRENT_PREFIX/_local_setup_util_sh.py" sh)"
unset _colcon_python_executable
if [ -n "$COLCON_TRACE" ]; then
echo "_colcon_prefix_sh_source_script() {
if [ -f \"\$1\" ]; then
if [ -n \"\$COLCON_TRACE\" ]; then
echo \"# . \\\"\$1\\\"\"
fi
. \"\$1\"
else
echo \"not found: \\\"\$1\\\"\" 1>&2
fi
}"
echo "# Execute generated script:"
echo "# <<<"
echo "${_colcon_ordered_commands}"
echo "# >>>"
echo "unset _colcon_prefix_sh_source_script"
fi
eval "${_colcon_ordered_commands}"
unset _colcon_ordered_commands
unset _colcon_prefix_sh_source_script
unset _colcon_prefix_sh_COLCON_CURRENT_PREFIX
- 先来看看第一部分
# since a plain shell script can't determine its own path when being sourced
# either use the provided COLCON_CURRENT_PREFIX
# or fall back to the build time prefix (if it exists)
_colcon_prefix_sh_COLCON_CURRENT_PREFIX="/root/dev_ws/install"
if [ -z "$COLCON_CURRENT_PREFIX" ]; then
if [ ! -d "$_colcon_prefix_sh_COLCON_CURRENT_PREFIX" ]; then
echo "The build time path \"$_colcon_prefix_sh_COLCON_CURRENT_PREFIX\" doesn't exist. Either source a script for a different shell or set the environment variable \"COLCON_CURRENT_PREFIX\" explicitly." 1>&2
unset _colcon_prefix_sh_COLCON_CURRENT_PREFIX
return 1
fi
else
_colcon_prefix_sh_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX"
fi
- 上述代码用于处理普通shell脚本在source时无法确定自己的路径的问题
_colcon_prefix_sh_COLCON_CURRENT_PREFIX="/root/dev_ws/install"
是脚本执行时使用的默认路径。/root/dev_ws/install
其中dev_ws
是我自己的工作空间- 然后是解释下述是否为空和是否存在
COLCON_CURRENT_PREFIX
:用于指定当前ROS 2工作空间或软件包的路径。_colcon_prefix_sh_COLCON_CURRENT_PREFIX
:用于存储COLCON_CURRENT_PREFIX
的值。这个变量在脚本内部使用,用于确定当前脚本所在目录的路径
_colcon_prefix_sh_prepend_unique_value
- 接着我们来看看这个长函数
# function to prepend a value to a variable
# which uses colons as separators
# duplicates as well as trailing separators are avoided
# first argument: the name of the result variable
# second argument: the value to be prepended
_colcon_prefix_sh_prepend_unique_value() {
# arguments
_listname="$1"
_value="$2"
# get values from variable
eval _values=\"\$$_listname\"
# backup the field separator
_colcon_prefix_sh_prepend_unique_value_IFS="$IFS"
IFS=":"
# start with the new value
_all_values="$_value"
_contained_value=""
# iterate over existing values in the variable
for _item in $_values; do
# ignore empty strings
if [ -z "$_item" ]; then
continue
fi
# ignore duplicates of _value
if [ "$_item" = "$_value" ]; then
_contained_value=1
continue
fi
# keep non-duplicate values
_all_values="$_all_values:$_item"
done
unset _item
if [ -z "$_contained_value" ]; then
if [ -n "$COLCON_TRACE" ]; then
if [ "$_all_values" = "$_value" ]; then
echo "export $_listname=$_value"
else
echo "export $_listname=$_value:\$$_listname"
fi
fi
fi
unset _contained_value
# restore the field separator
IFS="$_colcon_prefix_sh_prepend_unique_value_IFS"
unset _colcon_prefix_sh_prepend_unique_value_IFS
# export the updated variable
eval export $_listname=\"$_all_values\"
unset _all_values
unset _values
unset _value
unset _listname
}
- 哎嘿,是不是感觉很熟悉,是的,和
/local_setup.bash
的_colcon_prefix_bash_prepend_unique_value函数
是一个样,那就不用多说了吧~
- 那么再看看底下的代码,是不是又很熟悉?没错,还是一个样子
# add this prefix to the COLCON_PREFIX_PATH
_colcon_prefix_sh_prepend_unique_value COLCON_PREFIX_PATH "$_colcon_prefix_sh_COLCON_CURRENT_PREFIX"
unset _colcon_prefix_sh_prepend_unique_value
# check environment variable for custom Python executable
if [ -n "$COLCON_PYTHON_EXECUTABLE" ]; then
if [ ! -f "$COLCON_PYTHON_EXECUTABLE" ]; then
echo "error: COLCON_PYTHON_EXECUTABLE '$COLCON_PYTHON_EXECUTABLE' doesn't exist"
return 1
fi
_colcon_python_executable="$COLCON_PYTHON_EXECUTABLE"
else
# try the Python executable known at configure time
_colcon_python_executable="/usr/bin/python3"
# if it doesn't exist try a fall back
if [ ! -f "$_colcon_python_executable" ]; then
if ! /usr/bin/env python3 --version > /dev/null 2> /dev/null; then
echo "error: unable to find python3 executable"
return 1
fi
_colcon_python_executable=`/usr/bin/env python3 -c "import sys; print(sys.executable)"`
fi
fi
- 那么最后一部分,其实和上述之前的代码大差不差,那我也不多说了
# function to source another script with conditional trace output
# first argument: the path of the script
_colcon_prefix_sh_source_script() {
if [ -f "$1" ]; then
if [ -n "$COLCON_TRACE" ]; then
echo "# . \"$1\""
fi
. "$1"
else
echo "not found: \"$1\"" 1>&2
fi
}
# get all commands in topological order
_colcon_ordered_commands="$($_colcon_python_executable "$_colcon_prefix_sh_COLCON_CURRENT_PREFIX/_local_setup_util_sh.py" sh)"
unset _colcon_python_executable
if [ -n "$COLCON_TRACE" ]; then
echo "_colcon_prefix_sh_source_script() {
if [ -f \"\$1\" ]; then
if [ -n \"\$COLCON_TRACE\" ]; then
echo \"# . \\\"\$1\\\"\"
fi
. \"\$1\"
else
echo \"not found: \\\"\$1\\\"\" 1>&2
fi
}"
echo "# Execute generated script:"
echo "# <<<"
echo "${_colcon_ordered_commands}"
echo "# >>>"
echo "unset _colcon_prefix_sh_source_script"
fi
eval "${_colcon_ordered_commands}"
unset _colcon_ordered_commands
unset _colcon_prefix_sh_source_script
unset _colcon_prefix_sh_COLCON_CURRENT_PREFIX
总结
/local_setup.bash
和/opt/tros/local_setup.bash
都确保了正确的环境配置setup.bash
依次执行两个脚本的原因
- 环境变量继承:通过首先执行
/opt/tros/local_setup.bash
,脚本确保了从该路径继承的所有环境变量和配置都被包含在内。(初始化整个ROS2环境) - 特定于当前路径的配置:随后执行
./local_setup.bash
,这个脚本针对当前目录下的特定环境进行配置。(对当前工作空间下的环境进行进一步设置) - 层次化的环境配置:这种层次化的配置方法允许在不同级别(例如系统级别和项目级别)上进行精细的环境管理。每个
local_setup.bash
脚本负责其所在目录下的环境配置,而上层的脚本则负责将它们整合起来。 - 避免重复配置:通过分别执行两个脚本,可以避免在一个脚本中处理所有配置可能导致的复杂性。
- 灵活性和可维护性:这种分层的配置方法提高了灵活性和可维护性。例如,如果
/opt/tros
目录下的配置发生变化,只需更新那个特定的local_setup.bash
脚本,而不需要修改项目级别的脚本。
总的来说,这种做法是为了确保环境配置的层次性和灵活性,同时避免重复和混淆。每个local_setup.bash
脚本负责其所在目录下的环境配置,而setup.bash
脚本则负责将它们按照正确的顺序整合起来。
预告
- 下一期有空讲讲
_local_setup_util_sh.py
吧,多谢大家支持,如有错误,欢迎指正,谢谢。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
已为社区贡献15条内容
所有评论(0)