Shell脚本

Shell脚本
223
0
0
2024-05-19

1 Shell 脚本

Shell 脚本是运行在 shell 环境中的脚本语言,用于自动执行重复性任务、管理系统配置、以及通过编写脚本和运行脚本来执行一系列命令。shell 脚本可以帮助我们完成系统管理、软件安装、文件操作等需求。

Shell 脚本文件以 .sh 作为扩展名,基本语法如下:

#!/bin/bash

# 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 变量赋值:

#!/bin/bash

# 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 显示输入的内容:

#!/bin/bash

echo "Enter the username: "
read
echo "Read without variable name assignment: "$REPLY

5 命令替换

通过命令替换的形式可以将命令的输出赋值给变量。命令替换有以下两种方式:

  • 使用一对反撇号 ``
  • 使用 $()

例如,将 pwd 的输出赋值给 working_dir 变量:

#!/bin/bash

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 脚本中执行算术运算。(()) 也称为一元运算符。

#!/bin/bash

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 的文件,代码内容如下:

#!/bin/bash

# -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

elifelseif 的组合,用于创建多个条件语句,必须与 if else 语句结合使用

#!/bin/bash

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 循环。语法如下:

#!/bin/bash

for i in {1..10}
do
	echo "Var: $i"
done

while

while 循环用于在特定条件为真时重复执行一组命令,循环一直持续到条件为假时终止。

#!/bin/bash

count=0

while [ $count -lt 5 ]
do
	echo $count
	count=$(($count+1))
done

until

与 while 相反,在条件为假时重复执行一组命令,循环一直持续到条件为真时终止。

#!/bin/bash

count=1

until [ $count -gt 5 ]
do
	echo $count
	let count++
done

Break 语句

break 关键字是一个控制语句,用于在满足特定条件时退出循环(for、while 或 until)。break 仅退出循环,脚本将继续向下执行。

#!/bin/bash

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)中使用的关键字,用于跳过循环的当前迭代,进入下一次迭代。

#!/bin/bash

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 开始。
#!/bin/bash

# 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 函数

函数是一个代码块,可以重复使用来完成特定任务,从而提供了代码的可重用性。

以下是一个函数示例:

#!/bin/bash

sum() {
	echo "The numbers are: ${n1} ${n2}"
	sum_val=$((${n1}+${n2}))
	echo "Sum: $sum_val"
}

n1=$1
n2=$2
sum

带有返回值的函数

函数是可以有返回值的。要访问函数的返回值,需要使用 $?

#!/bin/bash

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 变量

变量是一个占位符,用于保存一个值,以后可以使用该名称访问该值。变量有两种类型:

  • 全局变量:在函数外部定义的变量,可在整个脚本中访问
  • 局部变量:定义在函数内部的变量,只能在函数内部访问
#!/bin/bash

# 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 命令用来定义字典:

#!/bin/bash

# 定义一个字典,并在定义后赋值
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 选项,它会捕获并立即停止脚本。因此,每条命令都应返回零退出代码。否则,脚本将失败。