1 Shell 脚本
Shell 脚本是运行在 shell 环境中的脚本语言,用于自动执行重复性任务、管理系统配置、以及通过编写脚本和运行脚本来执行一系列命令。shell 脚本可以帮助我们完成系统管理、软件安装、文件操作等需求。
Shell 脚本文件以 .sh
作为扩展名,基本语法如下:
# This is a note | |
echo "The first line of the script is 'shebang'" |
2 Shebang
shell 脚本的第一行是 shebang,#!/bin/bash
,它指定了执行脚本的解释器,通常是 bash。执行脚本时,内核会读取 shebang,并使用该解释器执行脚本。
可以使用以下命令查看系统使用、支持的 shell:
- echo $SHELL:显示当前使用的 shell 类型
- cat /etc/shells:显示操作系统中可用的 shell 类型
3 执行 Shell 脚本
可以通过以下方式使用 shebang 指定的 shell 执行脚本:
第一种方式,使用 sh 命令:
sh script_file.sh
第二种方式,通过相对路径或绝对路径:
- 首先为脚本文件添加可执行权限
chmod +x script_file.sh
- 通过相对或绝对路径,从终端执行脚本:
./script_file.sh
4 读取用户输入
read 命令可以从标准输入(stdin)读取用户输入的内容。比如输入用户名 weiwendi 为 shell 脚本中 username 变量赋值:
# displays the prompt message | |
# -p stand for prompt | |
# reads user input and assigns it to the newusername variable | |
read -p "Enter the username: " username | |
echo $username |
输入的内容将以明文显示在屏幕上,如果不希望显示输入的内容,比如密码之类需要保密的内容,可以使用 read -sp:
# reads input from the user & hides the text from echoing in the terminal | |
# -s stand for silent | |
read -sp "Enter Password: " password | |
echo "" | |
echo $password |
如果不想指定变量名,可以通过 $REPLY 显示输入的内容:
echo "Enter the username: " | |
read | |
echo "Read without variable name assignment: "$REPLY |
5 命令替换
通过命令替换的形式可以将命令的输出赋值给变量。命令替换有以下两种方式:
- 使用一对反撇号 ``
- 使用 $()
例如,将 pwd 的输出赋值给 working_dir
变量:
echo "Method 1 - Using backticks" | |
working_dir1=`pwd` | |
echo $working_dir1 | |
echo "Method 2 - Using $()" | |
working_dir2=$(pwd) | |
echo $working_dir2 |
6 参数传递
可以通过传递参数的形式,为脚本提供运行时所需的参数。脚本可以使用特殊变量如
、2、$3 等访问这些参数。
- $0:返回执行脚本的文件名
- $@:返回从 CLI 传递的所有参数
- $#:返回从 CLI 传递的参数数量
假设有一个名为 argument_passing.sh 的脚本文件,我们向它传递两个参数。
sh argument_passing.sh "Weiwendi" "DevOps Engineer" | |
#!/bin/bash | |
# get script file name | |
echo "FileName: " $0 # argument_passing.sh | |
# provide the first argument | |
echo "First Argument: "$1 # weiwendi | |
# provide the second argument | |
echo "Second Argument: "$2 # DevOps Engineer | |
# displays all arguments provided | |
echo "All Arguments: "$@ # weiwendi DevOps Engineer | |
# displays number of arguments provided | |
echo "Arguments Number: "$# # 2 |
7 算术运算
使用双括号 (())
可以在 shell 脚本中执行算术运算。(())
也称为一元运算符。
n1=10 | |
n2=5 | |
echo "Sum of two numbers: "$(($n1+$n2)) # Addition | |
echo "Sub of two numbers: "$(($n1-$n2)) # Substraction | |
echo "Mul of two numbers: "$(($n1*$n2)) # Mulitplication | |
echo "Div of two numbers: "$(($n1/$n2)) # Division | |
echo "Modulus of two numbers: "$(($n1%$n2)) # Modulus |
8 条件表达式
在 shell 脚本中,[[ ]]
或 test
命令可用于评估条件表达式。以下是一些用于测试条件的一元运算符
[[ -z String ]]:判断字符串是否为空。字符串为空,结果为 true。 | |
[[ -n String ]]:判断字符串是否不为空。字符串不为空,结果为 true。 | |
[[ String1 == String2 ]]:判断两个字符串是否相同。 | |
[[ String1 != String2 ]]:判断两个字符串是否不相同。 | |
[[ num1 -eq num2 ]]:判断两个数字是否相等。 | |
[[ num1 -ne num2 ]]:判断两个数字是否不相等。 | |
[[ num1 -lt num2 ]]:num1 是否小于 num2。 | |
[[ num1 -le num2 ]]:num1 小于或等于 num2。 | |
[[ num1 -gt num2 ]]:num1 大于num2。 | |
[[ num1 -ge num2 ]]:num1 大于或等于 num2。 | |
在 Linux 中,大多数对象以文件的形式存在,因此,Linux 也提供了对文件的条件判断: | |
[[ -e fileName ]]:判断文件是否存在 | |
[[ -r fileName ]]:对文件是否有读权限 | |
[[ -h fileName ]]:文件是否为符号链接 | |
[[ -d fileName ]]:是否为目录 | |
[[ -w fileName ]]:对文件是否有写权限 | |
[[ -s fileName ]]:文件是否大于 0 字节 | |
[[ -x fileName ]]:对文件是否有执行权限 |
if else
if else 是条件语句,可以根据条件的 true 或 false 执行不同的命令。
创建一个名为 ifelse.sh 的文件,代码内容如下:
# -e stands for exists | |
# use brackets to determine whether the file exists | |
if [[ -e ./ifelse.sh ]] | |
then | |
echo "File exist" | |
else | |
echo "File does not exist" | |
fi | |
# run the test command to check whether the file exists | |
if test -e "./ifelse.sh" | |
then | |
echo "File exist" | |
else | |
echo "File does not exist" | |
fi |
elif
elif 是 else 和 if 的组合,用于创建多个条件语句,必须与 if else 语句结合使用
read -p "Enter the number between 1 to 3: " number | |
if [[ $number -eq 1 ]] | |
then | |
echo "The number entered is 1" | |
elif [[ $number -eq 2 ]] | |
then | |
echo "The number entered is 2" | |
elif [[ $number -eq 3 ]] | |
then | |
echo "The number entered is 3" | |
else | |
echo "Invalid Number" | |
fi |
9 循环
在 shell 编程中,除了条件判断外,对一些特殊情况需要进行循环操作。
for
for 循环用于遍历列表,在进入 shell 循环前知道迭代次数时,通常使用 for 循环。语法如下:
for i in {1..10} | |
do | |
echo "Var: $i" | |
done |
while
while 循环用于在特定条件为真时重复执行一组命令,循环一直持续到条件为假时终止。
count=0 | |
while [ $count -lt 5 ] | |
do | |
echo $count | |
count=$(($count+1)) | |
done |
until
与 while 相反,在条件为假时重复执行一组命令,循环一直持续到条件为真时终止。
count=1 | |
until [ $count -gt 5 ] | |
do | |
echo $count | |
let count++ | |
done |
Break 语句
break 关键字是一个控制语句,用于在满足特定条件时退出循环(for、while 或 until)。break 仅退出循环,脚本将继续向下执行。
count=1 | |
while true | |
do | |
echo "Count is $count" | |
count=$(($count+1)) | |
if test $count -gt 5; then | |
echo "Break statement reached" | |
break | |
fi | |
done | |
echo "Jump out of the sequence and continue to execute the following code." |
Continue 语句
continue 是循环(如 for、while 和 until)中使用的关键字,用于跳过循环的当前迭代,进入下一次迭代。
for i in {1..10} | |
do | |
if [ $i -eq 5 ] | |
then | |
continue | |
fi | |
echo $i | |
done |
数组
数组中可以存储多个值,Bash shell 支持一维数组。在脚本中,通常把数组作为变量的值。
- ${arrayVarName[@]}:显示数组变量中的所有值
- ${#arrayVarName[@]}:显示数组的长度
- ${arrayVarName[0]}:显示数组的第一个元素
- ${arrayVarName[-1]}:显示数组的最后一个元素
- unset arrayVarName[2]:删除第三个元素,索引从 0 开始。
# Declare an array of fruits | |
fruits=("apple" "banana" "orange" "durian") | |
# Print array's element | |
echo "All fruits using @ symbol: ${fruits[@]}" | |
echo "All fruits using * symbol: ${fruits[*]}" | |
# Print the third element | |
echo "Third fruit: ${fruits[2]}" | |
# Delete the third element | |
unset fruits[2] | |
echo "All fruits: ${fruits[@]}" | |
# Print the length of the array | |
echo "Number of fruits: ${#fruits[@]}" |
10 函数
函数是一个代码块,可以重复使用来完成特定任务,从而提供了代码的可重用性。
以下是一个函数示例:
sum() { | |
echo "The numbers are: ${n1} ${n2}" | |
sum_val=$((${n1}+${n2})) | |
echo "Sum: $sum_val" | |
} | |
n1=$1 | |
n2=$2 | |
sum |
带有返回值的函数
函数是可以有返回值的。要访问函数的返回值,需要使用 $?
sum(){ | |
echo "The numbers are: $n1 $n2" | |
sum_val=$(($n1+$n2)) | |
return $sum_val | |
} | |
n1=$1 | |
n2=$2 | |
sum | |
echo "Retuned value from function is $?" |
11 变量
变量是一个占位符,用于保存一个值,以后可以使用该名称访问该值。变量有两种类型:
- 全局变量:在函数外部定义的变量,可在整个脚本中访问
- 局部变量:定义在函数内部的变量,只能在函数内部访问
# x & y are global variables | |
x=10 | |
y=20 | |
sum() { | |
sum=$(($x+$y)) | |
echo "Global Variable Addition: $sum" | |
} | |
sum | |
sub() { | |
# a & b are local variables | |
local a=20 | |
local b=10 | |
local sub=$(($a-$b)) | |
echo "Local Variable Substraction: $sub" | |
} | |
sub |
12 字典
在 shell 脚本中,字典是使用关联数组实现的。关联数组是使用字符串而不是整数作为索引的数组。declare -A 命令用来定义字典:
# 定义一个字典,并在定义后赋值 | |
declare -A dic1 | |
dic1[name]=Curry | |
dic1[no]=30 | |
# 根据 key 打印 value | |
echo "the name's: ${dic1[name]}" | |
echo "the jersey number's: ${dic1[no]}" | |
# 定义字典并同时赋值 | |
declare -A dic2=([name]=Wiggins [no]=22) | |
# 打印字典中的所有 key | |
echo "print all keys in the dic2: ${!dic2[@]}" | |
# 打印字典中的所有 value | |
echo "print all values in the dic2: ${dic2[@]}" | |
# 打印键值对 | |
for i in ${!dic2[@]} | |
do | |
echo $i: ${dic2[$i]} | |
done |
13 Set 选项
set 命令可以修改或显示 shell 选项的值。如果不带任何参数,将列出所有 shell 变量及值。 | |
set -x 类似于调试模式,先打印正在执行的命令,然后显示命令的输出结果。 | |
set -e 当出现非零退出代码时,立即退出脚本。 | |
在使用管道命令时,例如 sdfdsf | echo 'vish'。由于该行执行的最后一条命令是 echo,而 echo 返回的退出代码为零,因此整行命令被认为是成功的,但之前的命令 sdsds 将返回非零代码,这是错误的。要解决这个问题,我们可以使用下面的设置选项。 | |
set -o pipefail 为了克服上述管道命令错误,可以使用 set -o pipefail 选项,它会捕获并立即停止脚本。因此,每条命令都应返回零退出代码。否则,脚本将失败。 |