一篇文章搞定ROS2的setup.bash

引入

  • 相信学过ROS2的各位都十分熟悉一下的这条指令,在每次执行一些ROS2操作的时候都需要先执行这条刷新环境变量的脚本,那么这个脚本究竟做了什么,今天我们来看一看。
source ./install/setup.bash

Shell

  • Bash是Shell的一种,Shell是操作系统与用户之间的接口,允许用户通过命令行输入命令来控制计算机。Shell不仅限于Bash,还有其他多种类型,每种Shell都有其特点和功能。
    Shell的种类
  1. Bash (Bourne-Again SHell):是Bourne Shell(sh)的增强版本,是最流行的Linux Shell之一。它是一个命令行解释器,用户可以通过它输入命令,并与操作系统交互。
  2. Bourne Shell (sh):是原始的Unix Shell,由Steve Bourne在贝尔实验室开发。
  3. C Shell (csh):提供类似C语言的语法,适合编程。
  4. Korn Shell (ksh):结合了Bourne Shell和C Shell的特性,提供高级编程功能。
  5. 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脚本,接下来我们将逐行分析,先来看看这个脚本到底做了什么
    1. 环境扩展:脚本的主要功能是扩展当前的环境设置。在Linux和Unix系统中,环境通常包括一系列变量,如PATHLD_LIBRARY_PATH等,它们决定了系统如何解释和执行用户的命令。
    2. 其他前缀路径:脚本会包括(或“source”)在生成这个脚本时已经设置的其他路径。这意味着,如果之前有其他的脚本或设置已经被加载到环境中,这个脚本会确保这些设置被保留和继承。
    3. 包含的功能包:脚本不仅包括路径设置,还包括所有在这个路径下的功能包。
# 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/nullcd命令的输出重定向到/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/binPATH中,但是要求这个新的路径不能已经存在于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; dofor循环,_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软件包。
  1. 检查当前用户权限:脚本首先检查当前登录的用户是否是root用户。如果不是,脚本会提示用户使用root账户登录,并重新运行source /opt/tros/setup.bash命令。
  2. 设置环境变量COLCON_CURRENT_PREFIX:脚本确定当前脚本的目录路径,并将其存储在环境变量COLCON_CURRENT_PREFIX中。
  3. 处理环境变量COLCON_PYTHON_EXECUTABLE:脚本检查是否存在自定义的Python解释器路径。如果存在,脚本使用该路径;否则,尝试使用系统默认的Python解释器。
  4. 执行拓扑排序的命令:脚本使用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依次执行两个脚本的原因
  1. 环境变量继承:通过首先执行 /opt/tros/local_setup.bash,脚本确保了从该路径继承的所有环境变量和配置都被包含在内。(初始化整个ROS2环境)
  2. 特定于当前路径的配置:随后执行 ./local_setup.bash,这个脚本针对当前目录下的特定环境进行配置。(对当前工作空间下的环境进行进一步设置)
  3. 层次化的环境配置:这种层次化的配置方法允许在不同级别(例如系统级别和项目级别)上进行精细的环境管理。每个 local_setup.bash 脚本负责其所在目录下的环境配置,而上层的脚本则负责将它们整合起来。
  4. 避免重复配置:通过分别执行两个脚本,可以避免在一个脚本中处理所有配置可能导致的复杂性。
  5. 灵活性和可维护性:这种分层的配置方法提高了灵活性和可维护性。例如,如果 /opt/tros 目录下的配置发生变化,只需更新那个特定的 local_setup.bash 脚本,而不需要修改项目级别的脚本。
    总的来说,这种做法是为了确保环境配置的层次性和灵活性,同时避免重复和混淆。每个 local_setup.bash 脚本负责其所在目录下的环境配置,而 setup.bash 脚本则负责将它们按照正确的顺序整合起来。

预告

  • 下一期有空讲讲_local_setup_util_sh.py吧,多谢大家支持,如有错误,欢迎指正,谢谢。
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值