0 前言
最近没啥可以写博客的好点子,为此一直咕咕咕了。端午节没啥事,左右苦思冥想,发现网上针对ROS的bash启动脚本几乎还是处于0的状态。为此针对性的给ROS开一个bash脚本的教程是非常有必要的。也希望各位大佬能提点意见,如果后续合适,我会继续根据各位的意见来继续开坑的。
1 bash 脚本简介
1.1 bash 脚本基础介绍
首先我们知道常见的bash脚本是基于shell文件的。因为bash是免费的并且很容易使用。所以很多开源作者所提供的脚本都是使用bash。为此我们非常有必要去对该脚本语言结构进行详细阐述
-
程序必须以下面的行开始(必须方在文件的第一行)。符号
#!
用来告诉系统它后面的参数是用来执行该文件的程序。在这个例子中我们使用/bin/sh
来执行程序:#!/bin/sh
bash提供一些可选项帮助你调试程序,
-x option
它在执行时打印命令和参数。它被称为打印调试,跟踪或x跟踪。我们可以通过修改第一行来使用它;-e option
它代表“出错”。如果命令以非零退出状态退出,这将导致脚本立即退出。-v option
它在读取时打印shell命令/输入行。这些选项可以组合使用,一次可以使用多个选项!#!/bin/bash-xe #!/bin/bash-ex #!/bin/bash-x-e #!/bin/bash-e-x
-
在进行shell编程时,以#开头的句子表示注释,直到这一行的结束。bash脚本的注释非常有必要,也为了未来我们去分析我们所写脚本的作用及工作原理。
-
脚本执行,我们可以通过两种方式,来运行
filename
文件:chmod +x filename ./filename
1.2 bash 脚本基础语句介绍
-
bash脚本也可以对变量赋值,但是注意,等号两边不应有空格。同时bash中的语句结尾不需要分号
(";")
。num="hello world" NUM=$(num)#该命令执行后的输入结果赋值给一个变量 read -p "Please Enter You Name: " NAME #read 命令接收键盘的输入 echo "Your Name Is: $NAME"
-
如果我们需要打印变量a的内容,则可以用
echo
来实现文本的打印#变量分开打印 num=2 echo "NUM is:" echo $num #一行打印 num=2 echo "this is the "$num echo "this is the ${num}nd"
-
$
符号在bash文件中起到非常重要的作用。当我们在执行脚本时有时很想传个参数进去,如:#sh mysh.sh abc
那这时候需要用到$
符号$# #传入脚本的命令行参数个数 $* #所有命令行参数值,在各个参数值之间留有空格 $0 #命令本身(shell文件名) $1 #第一个命令行参数 $2 #第二个命令行参数
我们可以用以下脚本进行测试
sh my.sh a b c d e
my.sh
#!/bin/sh echo "number of vars:"$# echo "values of vars:"$* echo "value of var1:"$1 echo "value of var2:"$2 echo "value of var3:"$3 echo "value of var4:"$4 echo "value of var4:"$100 #执行时并没有输入100个参数,那取得的值为 NULL
另外还有其他的
$
符号指令$$ #Shell本身的PID(ProcessID,即脚本运行的当前 进程ID号) $! #Shell最后运行的后台Process的PID(后台运行的最后一个进程的 进程ID号) $? #最后运行的命令的结束代码(返回值)即执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误) $* #所有参数列表。如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数,此选项参数可超过9个。 $@ #所有参数列表。如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。 #$@ 跟$*类似,但是可以当作数组用
-
文件&字符串&算术等测试操作,主要方便调试判断的使用
#文件测试操作 -d FILE_NAM # True if FILE_NAM is a directory -e FILE_NAM # True if FILE_NAM exists -f FILE_NAM # True if FILE_NAM exists and is a regular file -r FILE_NAM # True if FILE_NAM is readable -s FILE_NAM # True if FILE_NAM exists and is not empty -w FILE_NAM # True if FILE_NAM has write permission -x FILE_NAM # True if FILE_NAM is executable #字符串测试操作 -z STRING # True if STRING is empty -n STRING # True if STRING is not empty STRING1 = STRIN2 # True if strings are equal STRING1 != STRIN2 # True if strings are not equal #算术测试操作 var1 -eq var2 # True if var1 is equal to var2 var1 -ne var2 # True if var1 not equal to var2 var1 -lt var2 # True if var1 is less than var2 var1 -le var2 # True if var1 is less than or equal to var2 var1 -gt var2 # True if var1 is greater than var2 var1 -ge var2 # True if var1 is greater than or equal to var2
-
条件判断,和其他编程语言一样,shell脚本也能基于条件进行判断,我们可以使用
if-else
或if-elif-else
来实现简单的条件判断if [ condition-is-true ] then command 1 elif [ condition-is-true ];then command 2 elif [ condition-is-true ] then command 3 else command 4 fi
-
case
语句case
可以实现和if一样的功能,但是当条件判断很多的时候,使用if不太方便,比如使用if进行值的比较#!/bin/bash read -p "Enter the answer in Y/N: " ANSWER case "$ANSWER" in [yY] | [yY][eE][sS])#大小写 echo "The Answer is Yes :)" ;; [nN] | [nN][oO]) echo "The Answer is No :(" ;; *) echo "Invalid Answer :/" ;; esac
-
迭代语句,可以通过循环执行同一个代码块很多次
COLORS="red green blue" for COLOR in $COLORS do echo "The Color is: ${COLOR}" done
-
while 循环,当所给的条件为true时,循环执行while里面的代码块,下面的例子是一行一行读取文件内容
#!/bin/bash LINE=1 while read CURRENT_LINE do echo "${LINE}: $CURRENT_LINE" ((LINE++)) done < /etc/passwd # This script loops through the file /etc/passwd line by line
-
退出状态码,任何一个命令执行完成后都会产生一个退出状态码,范围0-255,状态码可以用来检查错误 0 表示正确执行并正常退出,如果是非0,表示执行过程中出错,没有正常退出。例如我们可以通过错误码来检查主机和服务器之间是否可以抵达。
HOST="google.com" ping -c 1 $HOST # -c is used for count, it will send the request, number of times mentioned RETURN_CODE=$? # 最后运行的命令的结束代码(返回值)即执行上一个指令的返回值 if [ "$RETURN_CODE" -eq "0" ] then echo "$HOST reachable" else echo "$HOST unreachable" fi
自定义退出状态码默认的状态码是上一条命令执行的结果,我们可以通过
exit
来自定义状态码exit 0 exit 1 exit 2 ... ... exit 255
bash
脚本支持逻辑与&&
和逻辑或||
-
bash脚本中可以使用函数来把一些列的命令或语句定义在一个函数内,从程序的其他地方调用。
如果需要调用函数,我们只需要简单的给出函数名字即可调用#!/bin/bash function myFunc () { echo "Shell Scripting Is Fun!" } myFunc # call
同时脚本一样,也可以给函数传递参数完成特殊的任务,第一个参数存储在变量
$1
中,第二个参数存储在变量$2
中,$@
存储所有的参数,参数之间使用空格分割myFunc param1 param2 param3...
-
在上面第六点case的例子中,我们就是使用到了通配符这一概念。
bash
脚本使用通配符可以完成特定的匹配。#通配符* 可以通配一个或多个任意字符 *.txt hello.* great*.md #通配符?匹配一个字符 ?.md Hello? #通配符[]匹配括号内部的任意一个字符 He[loym], [AIEOU] #通配符[!]不匹配括号内的任何字符 `[!aeiou]` #匹配通配符。有些情况下我们想匹配*或?等特殊字符,可以使用转义字符\*\?
-
set
命令是ash
脚本的重要环节,却常常被忽视,导致脚本的安全性和可维护性出问题。和1.1处介绍类似。set -u # 脚本在头部加上它,遇到不存在的变量就会报错,并停止执行。 set -o nounset #与set -u等价 foo # 如果是不存在的命令,执行时会报错。但是,Bash 会忽略这个错误,继续往下执行。 set +e # +e表示关闭-e选项,set -e表示重新打开-e选项 command1 command2 set -e# 脚本只要发生错误,就终止执行
set -x #用来在运行结果之前,先输出执行的那一行命令 set -o xtrace#与set -x等价
-
Unix命令同样可以在bash脚本中使用。这些命令通常是用来进行文件和文字操作的。
ls #文件列表 wc –l filewc -w filewc -c sourcefile #计算文件行数计算文件中的单词数计算文件中的字符数 cp sourcefile destfile #文件拷贝 mv oldname newname #重命名文件或移动文件 rm file #删除文件 grep 'pattern' sourcefile #在文件内搜索字符串比如:grep 'searchstring' file.txt cut -b colnum sourcefile #指定欲显示的文件内容范围,并将它们输出到标准输出设备比如:输出每行第5个到第9个字符cut -b5-9 file.txt千万不要和cat命令混淆,这是两个完全不同的命令 cat file.txt #输出文件内容到标准输出设备(屏幕)上 file somefile #得到文件类型 read var #提示用户输入,并将输入赋值给变量 sort file.txt #对file.txt文件中的行进行排序 uniq #删除文本文件中出现的行列比如: sort file.txt | uniq expr #进行数学运算Example: add 2 and 3expr 2 "+" 3 find #搜索文件比如:根据文件名搜索find . -name filename -print tee #将数据输出到标准输出设备(屏幕) 和文件比如:somecommand | tee outfile basename sourcefile #返回不包含路径的文件名比如: basename /bin/tux将返回 tux dirname sourcefile #返回文件所在路径比如:dirname /bin/tux将返回 /bin head sourcefile #打印文本文件开头几行 tail sourcefile #打印文本文件末尾几行 grep "hello" file.txt | wc -l #管道 (|) 将一个命令的输出作为另外一个命令的输入。在file.txt中搜索包含有”hello”的行并计算其行数。 export MYENV=7 #定义环境变量并赋值,可以在bash文件之间互传 export -p #列出当前的环境变量 uname #可显示电脑以及操作系统的相关信息;-a或--all 显示全部的信息。-m或--machine 显示电脑类型。-n或-nodename 显示在网络上的主机名称。-r或--release 显示操作系统的发行编号。-s或--sysname 显示操作系统名称。 > #写入文件并覆盖旧文件 >> #加到文件的尾部,保留旧文件内容。
15.Bash脚本常见用法扩展,这位作者充分的去举了很多经典的bash脚本开发例子。需要进阶的读者可以看一下这一篇文章。
2 bash在ros的使用
在介绍完bash的基础用法后,我们对bash有了一定基础的认识。下面我来给各位读者提供一些经典的和ros开发相关的bash代码例子。
-
安装
ros melodic
环境(需要管理员启动)#!/bin/bash # ROS Melodic Installation Script sudo -s # Setup your sources.list sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list' # Set up your keys apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654 # Update packages apt-get update # Install ROS sudo apt install ros-melodic-desktop-full sudo apt install ros-melodic-desktop-full sudo apt install ros-melodic-desktop-full sudo apt install ros-melodic-desktop-full # Environment setup echo "# Source ROS installation setup" >> ~/.bashrc echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc source ~/.bashrc # Dependencies for building packages apt install python-rosdep python-rosinstall python-rosinstall-generator python-wstool build-essential # Install rosdep apt install python-rosdep # Initialize rosdep rosdep init rosdep update # Print final message echo "Script execution finalized"
-
创建ros开发环境
#!/bin/bash set -e SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # get UBUNTU_CODENAME, ROS_DISTRO, REPO_DIR, CATKIN_DIR export UBUNTU_CODENAME=$(lsb_release -s -c) case $UBUNTU_CODENAME in trusty) export ROS_DISTRO=indigo;; xenial) export ROS_DISTRO=kinetic;; bionic) export ROS_DISTRO=melodic;; *) echo "Unsupported version of Ubuntu detected. Only trusty (14.04.*), xenial (16.04.*), and bionic (18.04.*) are currently supported." exit 1 esac export REPO_DIR=$(dirname "$SCRIPT_DIR") export CATKIN_DIR="$HOME/catkin_ws" source /opt/ros/$ROS_DISTRO/setup.bash main() { install_catkin_tools create_catkin_ws } install_catkin_tools() { # Check if already installed if type catkin > /dev/null 2>&1; then echo "Catkin tools is already installed" else echo "Installing catkin tools ..." sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu `lsb_release -sc` main" > /etc/apt/sources.list.d/ros-latest.list' wget -qO - http://packages.ros.org/ros.key | sudo apt-key add - sudo apt-get -qq update sudo apt-get -qq install python-catkin-tools > /dev/null echo "Catkin tools installed successfully." fi } create_catkin_ws() { # Check if workspace exists if [ -e "$CATKIN_DIR/.catkin_workspace" ] || [ -d "$CATKIN_DIR/.catkin_tools" ]; then echo "Catkin workspace detected at ~/catkin_ws" else echo "Creating catkin workspace in $HOME/catkin_ws ..." source /opt/ros/$ROS_DISTRO/setup.bash mkdir -p "$HOME/catkin_ws/src" cd "$HOME/catkin_ws" catkin init > /dev/null catkin_make echo "Catkin workspace created successfully." fi } main
-
自动拉文件并编译,这部分我们展示下多文件的相互调用(主要展示下export的调用)
首先创建一个
ubuntu_test.bash
的文件#!/bin/bash set -e export UBUNTU_CODENAME=$(lsb_release -s -c) case $UBUNTU_CODENAME in trusty) export ROS_DISTRO=indigo;; xenial) export ROS_DISTRO=kinetic;; bionic) export ROS_DISTRO=melodic;; *) echo "Unsupported version of Ubuntu detected. Only trusty (14.04.*), xenial (16.04.*), and bionic (18.04.*) are currently supported." exit 1 esac export REPO_DIR=$(dirname "$SCRIPT_DIR") export CATKIN_DIR="$HOME/catkin_ws"
再创建一个
pull_package.bash
的bash文件#!/bin/bash set -e SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # get UBUNTU_CODENAME, ROS_DISTRO, REPO_DIR, CATKIN_DIR source $SCRIPT_DIR/identify_environment.bash cd "$HOME/catkin_ws/src" rm CMakeLists.txt git clone https://github.com/xxxx/xxxxxxxx.git . # Build the whole workspace echo "building all packages" cd $HOME/catkin_ws catkin_make echo "built all packages" # Source the workspace source $HOME/catkin_ws/devel/setup.bash
-
安装第三方库,以
ceres
为例#!/bin/bash set -e BUILD_TYPE=Release INSTALL_PREFIX=${PWD} # Clone if [ ! -d src/ceres-solver ]; then cd src git clone https://github.com/ceres-solver/ceres-solver cd .. fi # Build cd src/ceres-solver git checkout 2.0.0 rm -rf build mkdir -p build cd build cmake .. \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX \ -DCMAKE_PREFIX_PATH=$INSTALL_PREFIX make make install
- ros卸载
在写这篇博客的同时,本人发现网络上有一个对机器人函数进行安装的开源项目。如果各位有需求可以访问该项目直接根据本文的知识对所需要的函数库进行安装:Ubuntu Setup Scripts for Robotics & Machine Learning#!/bin/bash apt-get purge ros-* rm -rf /etc/ros gedit ~/.bashrc
评论(0)
您还未登录,请登录后发表或查看评论