Shell 是一个应用程序,充当用户界面和脚本解释器,是用户使用 Linux 系统的桥梁。大多数 Linux 发行版将 Bash 作为默认的 Shell 程序,本文将以 Bash 为例,介绍如何在 Linux 环境进行 Shell 编程。Shell 脚本是一种以 Shell 程序为解释器的脚本程序。由于习惯的原因,业界所说的”Shell 编程”都是指编写 Shell 脚本,不是指 Shell 程序扩展开发。
1 一个简单的 Hello World 脚本
创建一个名为 hello.sh 的文本文件,输入一下内容并保存退出。
# Hello world demo #!/bin/bash echo 'Hello world!'
第一行以 # 开头表示注释,说明脚本的功能;
第二行以 #! 开头指定使用的 Shell 程序;
最后一行使用 echo 命令向屏幕打印 “Hello World!”。
shell脚本的执行有两种方式,一种是显示使用 Shell 程序执行脚本,另一种方式是给脚本添加可执行权限后直接执行脚本。
# 1. 使用 Shell 程序执行脚本 # 使用 bash 执行脚本 bash hello.sh # 2. 先为脚本添加可执行权限,然后直接执行脚本 chmod +x hello.sh ./hello.sh
其中第二种方式直接执行脚本需要指定脚本所在路径,否则将在 Path 环境变量中检索脚本。
2 变量
2.1 定义变量
Shell 脚本中使用如下方式定义变量,注意变量名和等号之间不能有空格。
# 直接定义变量 my_name="Bob" # 在语句中给变量赋值,例如在 for 循环中给变量赋值 for file in `ls /etc` for file in ./xxx
2.2 使用变量
使用一个定义过的变量,只需要在变量名前面加美元符号即可,例如:
my_name="Bob" # 定义字符串变量 my_name echo $my_name # 打印 my_name 的值 # 可以使用 export 将局部变量转换为全局变量,可以使用 env 命令查看所有的全局变量 export my_name
2.3 重新定义变量
直接给已经定义的变量赋值就好。
my_name="Bob" echo <span class="katex math inline">my_name my_name="Tom" # 重新给变量赋值 echo</span>my_name
3 注释
以 # 号开头的行就是注释,Shell 里没有多行注释,只能每一行加一个 #,如下:
# 这是一行注释 # 这也是一行注释
4 字符串
Shell 中的字符串可以用单引号,可以用双引号,也可以不用引号。
4.1 单引号字符串
str='This is a string.' echo <span class="katex math inline">str my_name="Bob" str='I am</span>my_name' echo $str
单引号字符串的限制:
- 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
- 单引号字符串中不能出现单引号,单引号中的单引号将会被忽略(对单引号使用转义字符也不行)
4.2 双引号字符串
my_name="Bob" greeting="Hello, I am <span class="katex math inline">my_name!\n" echo</span>greeting my_name="Tom" greeting="Hello, I am <span class="katex math inline">{my_name}!\n" echo</span>greeting
- 双引号字符串里可以有变量
-
双引号字符串里可以出现转义字符
4.3 不加引号的字符串
不加引号的字符串不会将含有空格的字符串视为一个整体输出, 如果内容中有变量等,会先把变量解析出结果,然后在输出最终内容来,如果字符串中带有空格等特殊字符,则不能完整的输出,需要改加双引号,一般连续的字符串,数字,路径等可以用。
my_name="Bob" # 不加引号字符串中的变量前不用加<span class="katex math inline">str=+++++name+++++ echo</span>str
4.4 反引号的字符串
如果需要调用命令的输出,或把命令的输出赋给变量,则命令必须使用反引号包含,这条命令才会执行。反引号的作用和 (命令) 是一样的,但是反引号非常容易和单引号搞混,所以推荐使用(命令) 的方式引用命令的输出。
# date被当做字符串 echo "date" # 输出日期时间 echo `date` echo $(date)
4.5 字符串操作
字符串操作包括拼接字符串、获取字符串的长度、提取子串、查找子串等。
# 字符串拼接 my_name="Bob" greeting="I am "<span class="katex math inline">my_name"!" echo</span>greeting greeting="I am <span class="katex math inline">{my_name}" echo</span>greeting # 获取字符串的长度 str="abcde" echo <span class="katex math inline">{#str} # 输出 5 # 提取子串</span>{字符串名:起始索引:子串长度} str="HUAWEI is a great company." echo <span class="katex math inline">{str:12:5} # 输出 great # 查找子串 str="HUAWEI is a great company." # 找出字母 H 的位置, 从 0 开始 echo</span>(expr index "$str" H) # 返回1
5 流程控制
和 Java 等语言不一样,Shell 的流程控制不可为空,如:
if (i == 0) { System.out.println("i is equal to 0") } else { // do nothing... }
在 Shell 中,这样写是不合法的,如果 else 分支没有语句执行,就不要写这个 else。
此外,Shell 中的方括号是一个可执行程序,在 CentOS/Ubuntu 中,它位于 /usr/bin 目录下:
ll /usr/bin/[ # 输出如下 -rwxr-xr-x 1 root root 55744 Apr 5 2024 '/usr/bin/['*
正因为方括号是一个可执行程序,方括号后面必须加空格,不能写成 if [$foo -eq 0]
5.1 if else
5.1.1 if
if condition then command1 command2 ... commandN fi
写成一行(适用于终端命令提示符):
if `ps -ef | grep ssh`; then echo hello; fi
末尾的 fi 就是 if 倒过来拼写,后面还会遇到类似的情况。
5.1.2 if else
if condition then command1 command2 ... commandN else command fi
5.1.3 if else-if else
if condition1 then command1 elif condition2 command2 else commandN fi
5.2 for while
5.2.1 for
for var in item1 item2 ... itemN do command1 command2 ... commandN done
写成一行:
for var in item1 item2 ... itemN; do command1; command2… done;
5.2.2 C 风格的 for
for (( EXP1; EXP2; EXP3 )) do command1 command2 command3 done
5.2.3 while
while condition do command done
5.3.4 死循环
while : do command done # 或者 while true do command done # 或者 for (( ; ; ))
5.3.5 until
until condition do command done
5.3.6 case 例子
case 的语法和 C family 语言差别很大,它需要一个 esac(就是 case 反过来)作为结束标记,每个 case 分支用右圆括号,用两个分号表示 break。
case "<span class="katex math inline">{opt}" in "Install-Puppet-Server" ) install_master</span>1 exit ;; "Install-Puppet-Client" ) install_client $1 exit ;; "Config-Puppet-Server" ) config_puppet_master exit ;; "Config-Puppet-Client" ) config_puppet_client exit ;; "Exit" ) exit ;; * ) echo "Bad option, please choose again" esac
6 文件包含
可以使用 source 和 . 关键字,例如:
source ./function.sh . ./function.sh
在 bash 里,source 和 . 是等效的,他们都是读入 function.sh 的内容并执行其内容,为了更好的可移植性,推荐使用第二种写法。
包含一个文件和执行一个文件一样,也要写这个文件的路径,不能光写文件名。
如果 function.sh 是用户传入的参数,如何获得它的绝对路径呢?方法是:
real_path=`readlink -f <span class="katex math inline">1`#</span>1是用户输入的参数,如function.sh . $real_path
参考资料
- 本文固定链接: https://weiguangli.com/archives/770
- 转载请注明: lwg0452 于 Weiguang的博客 发表