目录
一、前言
Shell脚本是一种用来执行命令行命令的脚本文件。它是由一系列Shell命令组成的程序,通常用于自动化任务、系统管理、数据处理等。Shell脚本可以运行在各种Unix和Linux系统上,也可以在Windows上的一些兼容环境(如Cygwin、WSL)中运行。
二 、组成部分
1、解释器声明:脚本文件的第一行通常是#!(称为shebang),后跟解释器路径,用于指定脚本运行时使用的解释器。例如,使用Bash解释器:
#!/bin/bash
2、命令:Shell脚本由一系列Shell命令组成,每个命令在脚本中独立执行。
echo "Hello, World!"
3、变量:可以定义和使用变量来存储数据。
NAME="John"
echo "Hello, $NAME"
4、控制结构:包括条件语句(if、case)、循环(for、while)。
if [ "$NAME" == "John" ]; then
echo "Hello, John!"
else
echo "Hello, $NAME!"
fi
5、函数等。
#!/bin/bash
# 定义函数
function greet {
echo "Hello, $1"
}
# 调用函数
greet "Alice"
greet "Bob"
三、执行方式的区别
1、./
- 用途:使用当前目录下的解释器来执行脚本或可执行文件。
- 解释器:取决于脚本的第一行(shebang)指定的解释器。例如,如果脚本的第一行是
#!/bin/bash
,那么使用./myscript.sh
会执行myscript.sh
文件,并使用Bash作为解释器。 - 适用性:主要用于执行当前目录下的脚本或二进制文件,比如用户自定义的Shell脚本或者可执行文件。
- 权限要求:使用
./
来执行脚本,必须确保脚本文件具有可执行权限。
2、bash
- 用途:显式地使用Bash解释器来执行脚本或命令。
- 解释器:强制使用Bash解释器执行。不管脚本的shebang是什么,都会使用Bash来解释执行脚本。
- 适用性:适合确保在不同系统上使用相同的Shell环境(Bash)来执行脚本,或者需要使用Bash特定功能的脚本。
- 权限要求:使用
bash
命令来执行脚本,不需要脚本文件本身有可执行权限,只要用户对脚本文件有读权限即可。这是因为bash
命令会直接解释执行文件,而不依赖文件自身的可执行权限。
3、sh
- 用途:使用系统默认的Shell解释器来执行脚本或命令,而且会用新的Shell进程启动。
- 解释器:通常指向系统上的标准Shell(可能是Bourne Shell或其衍生版本),不一定是Bash。
- 适用性:保证脚本在不同UNIX系统上的兼容性,因为大多数UNIX系统都会安装一个标准的Shell解释器,比如Bourne Shell (
sh
)或其替代品。 - 权限要求:使用
sh
命令来执行脚本,同样不需要脚本文件本身有可执行权限,只要用户对脚本文件有读权限即可。sh
命令也会直接解释执行文件,不依赖文件自身的可执行权限。
四、概念
1、输出
1.1 echo
输出特定信息或字符串。
-e
:启用解释反斜杠转义字符。-n
:不在末尾添加换行符。-E
:禁用解释反斜杠转义字符(默认行为)。
当使用 -e
选项时,以下转义字符可以被解释:
\a
:警告(响铃)\b
:退格\c
:抑制(不输出)其后的换行符\e
:转义字符\f
:换页\n
:换行\r
:回车\t
:水平制表符\v
:垂直制表符\\
:反斜杠\nnn
:八进制值 nnn(1 到 3 位数字)\xHH
:十六进制值 HH(1 到 2 位数字)
#基础语法
echo "hello world!"
#输出重定向到文件
echo "hello world!" > hello.txt
#输出追加到文件末尾
echo "hello world!" >> hello.txt
#输出变量值,会输出hello, Alice
name="Alice"
echo "hello, $name"
#使用-e选项启用转义字符解释,有个换行符号,所以会输出 hello
# world
echo -e "hello\nworld"
1.2 输出重定向
#将命令的输出写入文件,覆盖文件内容
echo "Hello, World!" > output.txt
# 将命令的错误输出写入文件,覆盖文件内容
ls file 2> error.txt
# 将标准输出和标准错误输出都写入文件,覆盖文件内容
ls file.txt non_existing_file > combined_output.txt 2>&1
#或
ls file.txt non_existing_file &> combined_output.txt
# 将标准输出追加到文件
echo "Hello, World!" >> output.txt
# 将标准错误输出追加到文件
echo "Hello, World!" 2>> output.txt
# 将标准输出和标准错误输出都追加到同一个文件
ls file.txt non_existing_file >> combined_output.txt 2>&1
#或
ls file.txt non_existing_file &>> combined_output.txt
# 将命令的输出写入文件并显示在终端,使用tee
echo "Hello, World!" | tee output.txt
# 将命令的输出追加到文件并显示在终端,使用tee -a
echo "Hello, World!" | tee -a output.txt
1.3 重定向静默模式
/dev/null
是一个特殊的设备文件,可以丢弃所有写入它的数据,相当于一个黑洞。你可以将命令的标准输出或标准错误输出重定向到 /dev/null
,这样就可以达到静默执行的效果。
#!/bin/bash
# 这里是一些输出
echo "Starting my script..."
# 静默执行命令,将输出重定向到 /dev/null
ls silence >/dev/null 2>&1
# 更多输出
echo "Script finished."
2、输入
2.1 read
主要用于从标准输入读取一行,并将输入赋值给一个或多个变量。
-p
:在读取输入前显示提示信息。-r
:禁止反斜杠转义(原样读取输入)。-n
:读取指定的字符数,而不是整行。-s
:隐藏输入的字符(适用于输入密码)。-t
:设置超时(秒),在超时后读取命令会自动退出。-d
:设置定界符,默认是换行符。
#读取一行输入并存储在一个变量中,回车结束输入
read name
echo "Hello, $name"
#提示信息,使用 -p 选项在读取前显示提示信息
read -p "Enter your age: " age
echo "You are $age years old."
#使用 -r 选项禁止反斜杠转义
read -r -p "Enter a file path: " filepath
echo "The file path is: $filepath"
#一次性读取多于一个变量
echo "Enter your first name and last name:"
read firstname lastname
echo "Hello, $firstname $lastname"
#设置读取字符数,使用 -n 选项读取指定数量的字符
read -n 4 -p "Enter a 4-digit PIN: " pin
echo "Your PIN is: $pin"
#使用 -s 选项隐藏输入(适用于密码)
read -s -p "Enter your password: " password
echo
echo "Your password is stored securely."
#设置超时,使用 -t 选项设置读取输入的超时时间(秒)
if read -t 5 -p "Enter your username (5 seconds timeout): " username; then
echo "Hello, $username"
else
echo "Timed out!"
fi
#使用 -d 选项设置自定义的定界符(默认是换行符)
read -d ';' -p "Enter input terminated by ';': " input
echo "You entered: $input"
#从文件中逐行读取输入
while read line; do
echo "Read line: $line"
done < input.txt
2.2 输入重定向
重定向输入是将文件的内容作为命令的输入,而不是从标准输入(键盘输入)获取内容。在 Shell 脚本中,可以使用输入重定向符号 <
来实现这一功能。
command < input_file
# command 是需要接收输入的命令或程序。
# < 是输入重定向符号。
# input_file 是包含输入内容的文件名
例子一
#!/bin/bash
# 假设 input.txt内容为 hello
cat < input.txt # 会将hello传递给cat命令然后显示在终端上
例子二
#!/bin/bash
grep "error" < logfile.txt # 输出 logfile.txt 中包含 "error" 关键词的所有行。
例子三
#!/bin/bash
while IFS= read -r line
do
echo "Line: $line"
done < input.txt # 传递给while循环遍历
看到这里可能有小伙伴会好奇例子一中的 cat < file 不是和 cat file 的效果一样吗,都是显示到终端,这里总结一下区别:
1)cat < file :使用了输入重定向,将文件的内容作为标准输入传递给cat命令了,虽然cat命令通常用于连接文件并输出他们的内容,但是在这种情况下,它是从标准输入中获取内容。
2)cat file:这种情况是直接将文件名作为cat命令的参数,cat命令会打开文件“file”并输出其内容。
3)当文件名以特殊字符开头(如 -
)时,直接使用 cat file可能会被解释为 cat
命令的选项(例如 cat -file
),而 cat < file 则不受影响,因为它将 file
视为文件名而不是选项。
4)在某些非交互式的上下文中,使用输入重定向可能更容易处理文件名中包含空格或特殊字符的情况,因为它避免了 Shell 解释器可能进行的特殊字符处理。
3、变量引用格式
在介绍变量前,很有必要了解一下变量的引用格式,细节是很重要的,主要有以下几种引用格式:
·基本引用: $xxx 直接引用
·大括号引用:${ },使用 ${ }可以明确变量的边界,避免与后续字符混淆。
·引号引用:单引号与双引号
·命令替换:使用反引号 `cmd` 或 $(cmd )
#!/bin/bash
# 基本引用和大括号引用
greeting="Hello"
name="World"
echo $greeting, $name! # 输出: Hello, World!
echo ${greeting}, ${name}! # 输出: Hello, World!
my_var="Hello, World!"
echo ${#my_var} # 输出: 13 (字符串长度) ,#用于获取长度
echo ${my_var:7:5} # 输出: World (子字符串)
# 引号引用
echo '$greeting, $name!' # 输出: $greeting, $name!
echo "$greeting, $name!" # 输出: Hello, World!
# 命令替换
current_date=$(date)
echo "Current date and time: $current_date"
# 嵌套命令替换
current_user=$(whoami)
home_dir=$(eval echo ~$current_user) # eval命令会将它的参数当作命令执行
echo "Current user's home directory: $home_dir"
# 计算命令结果
num_files=$(ls | wc -l)
echo "Number of files in current directory: $num_files"
#运行结果
# Hello, World!
# Hello, World!
# $greeting, $name!
# Hello, World!
# Current date and time: 2024年 06月 28日 星期五 11:01:42 CST
# Current user's home directory: /home/lmm
# Number of files in current directory: 4
4、变量
4.1 普通变量
普通变量用于存储数据,在当前Shell会话或脚本中使用。
my_var="Hello, World!"
number=42
echo $my_var # 输出: Hello, World!
echo $number # 输出: 42
4.2 环境变量
环境变量是在当前Shell会话及其子进程中都可以使用的变量。它们通常用于配置系统和程序的行为。常见的环境变量:
·PATH
:指定可执行文件的搜索路径。
·HOME
:当前用户的主目录。
·USER
:当前登录的用户名。
·SHELL
:当前用户的默认Shell。
·PWD
:当前工作目录。
·OLDPWD
:前一个工作目录。
#设置环境变量
export MY_ENV_VAR="Some value"
echo $MY_ENV_VAR # 输出: Some value
#添加到PATH
export PATH="/my/custom/dir:$PATH"
4.3 位置参数变量
位置参数变量用于传递给脚本或函数的参数。
$0
:脚本或当前Shell命令的名称。$1
-$9
:传递给脚本或函数的参数。$1
是第一个参数,$2
是第二个参数,依此类推。$#
:传递给脚本或函数的参数个数。$*
:所有参数作为一个字符串。"$@"
:所有参数,每个参数作为单独的字符串。
#!/bin/bash
echo "Script name: $0"
echo "First argument: $1"
echo "Second argument: $2"
echo "Number of arguments: $#"
echo "All arguments as a single string: $*"
for arg in "$@"; do
echo "All arguments as separate strings: $arg"
done
#执行 sh xxx.sh a b c d e
#结果为:
# Script name: file.sh
# First argument: a
# Second argument: b
# Number of arguments: 5
# All arguments as a single string: a b c d e
# All arguments as separate strings: a
# All arguments as separate strings: b
# All arguments as separate strings: c
# All arguments as separate strings: d
# All arguments as separate strings: e
4.4 特殊变量
特殊变量是由Shell自动定义的,提供有关Shell环境和执行状态的信息。
$$
:当前Shell进程的PID。$!
:上一个后台进程的PID。$?
:上一个命令的退出状态。0表示成功,非0表示失败。$_
:上一个命令的最后一个参数,该变量在使用不同的启动方式运行脚本获取的值会不一样,使用./运行脚本该变量值为上一条命令的最后参数,使用sh运行脚本该变量的值为shell启动的最后参数,通常是Shell解释器的路径,例如/bin/sh
或/bin/bash
。IFS
:内部字段分隔符,用于分割单词。默认是空格、制表符和换行符。
#!/bin/bash
echo "Last argument of the previous command: $_"
echo "Current PID: $$"
sleep 30 &
echo "PID of the last background job: $!"
ls ./
echo "Exit status of the last command: $?"
IFS=","
str="one,two,three"
for word in $str; do
echo "Word: $word"
done
cd /tmp
echo "Previous working directory: $OLDPWD"
cd - #返回前一个工作目录
#运行结果
# Last argument of the previous command: ./file.sh
# Current PID: 898930
# PID of the last background job: 89894
# file.sh file.txt gdb.c gdbtest
# Exit status of the last command: 0
# Word: one
# Word: two
# Word: three
# Previous working directory: /home/lmm/common
4.5 普通数组
普通数组使用整数作为索引,索引从0开始。
4.5.1 定义数组
# 定义数组
my_array=(one two three four)
# 定义一个包含三个元素的数组
fruits=("apple" "banana" "cherry")
# 定义一个空数组,然后逐个添加元素
fruits=()
fruits[0]="apple"
fruits[1]="banana"
fruits[2]="cherry"
4.5.2 访问数组
echo ${fruits[0]} # 输出: apple
echo ${fruits[1]} # 输出: banana
echo ${fruits[2]} # 输出: cherry
4.5.3 获取所有元素
echo ${fruits[@]} # 输出: apple banana cherry
echo ${fruits[*]} # 输出: apple banana cherry
4.5.4 获取数组长度
echo ${#fruits[@]} # 输出: 3
echo ${#fruits[*]} # 输出: 3
4.5.5 遍历数组
for fruit in "${fruits[@]}"; do
echo $fruit
done
#输出
# apple
# banana
# cherry
4.5.6 删除数组元素
unset fruits[1]
echo ${fruits[@]} # 输出: apple cherry
例子
#!/bin/bash
# 定义普通数组
fruits=("apple" "banana" "cherry")
# 访问数组元素
echo "First fruit: ${fruits[0]}"
# 获取数组长度
echo "Number of fruits: ${#fruits[@]}"
# 遍历数组
echo "All fruits:"
for fruit in "${fruits[@]}"; do
echo $fruit
done
4.6 关联数组
关联数组使用字符串作为索引。关联数组在Bash 4及以上版本中可用。
4.6.1 定义关联数组
使用 declare -A
关键字定义关联数组:
declare -A colors
colors=([red]="#FF0000" [green]="#00FF00" [blue]="#0000FF")
4.6.2 访问关联数组
可以通过数组名和字符串索引访问关联数组元素:
echo ${colors[red]} # 输出: #FF0000
echo ${colors[green]} # 输出: #00FF00
echo ${colors[blue]} # 输出: #0000FF
4.6.3 获取所有键
使用 ${!array[@]}
可以获取关联数组的所有键:
echo ${!colors[@]} # 输出: red green blue
4.6.4 获取所有值
使用 ${array[@]}
可以获取关联数组的所有值:
echo ${colors[@]} # 输出: #FF0000 #00FF00 #0000FF
4.6.5 遍历数组
for key in "${!colors[@]}"; do
echo "$key: ${colors[$key]}"
done
# 输出
# red: #FF0000
# green: #00FF00
# blue: #0000FF
4.6.6 删除数组元素
使用 unset
可以删除关联数组中的元素:
unset colors[green]
echo ${!colors[@]} # 输出: red blue
例子
#!/bin/bash
# 定义关联数组
declare -A colors
colors=([red]="#FF0000" [green]="#00FF00" [blue]="#0000FF")
# 访问关联数组元素
echo "Color red: ${colors[red]}"
# 获取关联数组的所有键
echo "All color keys: ${!colors[@]}"
# 遍历关联数组
echo "All colors:"
for key in "${!colors[@]}"; do
echo "$key: ${colors[$key]}"
done
5、控制结构
5.1 条件判断结构
5.1.0 常用的判断条件
条件表达式的返回值决定了代码的执行流,如果条件为真 (true),则返回 0;如果条件为假 (false),则返回非 0。以下为通用的判断条件,还有一些是只能在[[ ]] 结构中使用的。
符号 | 说明 |
-e | 判断文件是否存在 |
-f | 判断是否为普通文件 |
-d | 判断是否为目录 |
-b | 判断文件是否是块设备文件 |
-c | 判断文件是否是字符设备文件 |
-p | 判断文件是否是命名管道(FIFO) |
-S | 判断文件是否是套接字 |
-h/-L | 判断是否为符号链接 |
-r | 判断是否可读 |
-w | 判断文件是否可写 |
-x | 判断文件是否可执行 |
-s | 判断文件是否非空(不为空) |
file1 -nt file2 | 判断文件 file1 是否比 file2 新 |
file1 -ot file2 | 判断文件 file1 是否比 file2 旧 |
-z | 判断字符串是否为空 |
-n | 判断字符串是否非空 |
= | 判断字符串是否相等 |
!= | 判断字符串是否不等 |
-eq | 判断两个整数是否相等 |
-ne | 判断两个整数是否不等 |
int1 -lt int2 | 判断 int1 是否小于 int2 |
int1 -le int2 | 判断 int1 是否小于或等于 int2 |
int1 -gt int2 | 判断 int1 是否大于 int2 |
int1 -ge int2 | 判断 int1 是否大于或等于 int2 |
! | 逻辑非(取反) |
condition1 -a condition2 | 逻辑与(AND) |
condition1 -o condition2 | 逻辑或(OR) |
只能在[[ ]] 结构中使用的判断条件:
符号 | 说明 |
== | 判断字符串是否相等(支持通配符匹配) |
< | 判断字符串是否在字典序中小于另一个字符串(需要转义) |
> | 判断字符串是否在字典序中大于另一个字符串(需要转义) |
=~ | 判断字符串是否匹配正则表达式 |
&& | 逻辑与 |
|| | 逻辑或 |
5.1.1 if-else
#语法结构
if [ condition ]; then
# condition 为 true 时执行的命令
commands
elif [ another_condition ]; then
# another_condition 为 true 时执行的命令
commands
else
# condition 和 another_condition 都为 false 时执行的命令
commands
fi
# 例子1
#!/bin/bash
age=20
if [ $age -ge 18 ]; then
echo "yes"
else
echo "no"
fi
#例子2
#!/bin/bash
num=10
if [ $num -gt 15 ]; then
echo "The number is greater than 15."
elif [ $num -gt 5 ]; then
echo "The number is greater than 5 but less than or equal to 15."
else
echo "The number is less than or equal to 5."
fi
除了使用以上的写法还有另外一种语法格式如下:
if [[ condition ]]; then
# condition 为 true 时执行的命令
commands
elif [[ another_condition ]]; then
# another_condition 为 true 时执行的命令
commands
else
# condition 和 another_condition 都为 false 时执行的命令
commands
fi
可以看到判断条件变成了[[ ]],与[ ]的区别是什么呢?它们的优缺点是什么?
1)[[ ]] 是 Bash 的关键字,而 [ ]
是 POSIX 的标准命令。
2)
[[ ]]是一个更高级的条件测试命令,提供了更多的特性和更好的语法,不支持在没有Bash 的系统上使用。[ ]
是一个传统的测试命令,适用于大多数 UNIX 系统,包括那些没有 Bash 的系统。
3)在 [[ ]] 中可以直接使用 ==
和 !=
进行字符串比较,还可以使用模式匹配(带有通配符)。在 [ ]
中只能使用 =
和 !=
,且需要转义特殊字符。
4)在 [[ ]] 中可以直接使用 &&
和 ||
进行逻辑与和逻辑或操作。在[ ]
中需要使用 -a
和 -o
,但这些操作符可能会产生歧义,建议使用组合条件的方式避免。
5)在[[ ]] 处理未引用的变量更安全,即使变量未定义也不会报错。[ ]
处理未定义变量时需要小心,未定义变量会被解释为空字符串,这可能导致意外行为。
6)[[ ]] 支持正则表达式匹配,[ ]
不支持正则表达式匹配。
总结:
1)如果是在编写 Bash 脚本,并且需要使用正则表达式、复杂逻辑操作或处理未定义变量,建议使用 [[ ]] 。它提供了更强大的功能和更好的安全性。
2)如果需要编写高度兼容的脚本,适用于各种 UNIX 系统,包括那些没有 Bash 的系统,建议使用 [ ]
。它是 POSIX 标准,可以确保在更多环境中运行。
5.1.2 case
case variable in
pattern1)
# 当变量匹配 pattern1 时执行的代码
;;
pattern2)
# 当变量匹配 pattern2 时执行的代码
;;
pattern3 | pattern4)
# 当变量匹配 pattern3 或 pattern4 时执行的代码
;;
*)
# 当变量不匹配任何模式时执行的代码
;;
esac
# 例子1
#!/bin/bash
# 读取用户输入
echo "Enter a fruit (apple, banana, cherry):"
read fruit
# 使用 case 语句判断用户输入
case $fruit in
apple)
echo "You selected apple."
;;
banana)
echo "You selected banana."
;;
cherry)
echo "You selected cherry."
;;
*)
echo "Unknown fruit."
;;
esac
# 例子2,使用通配符
#!/bin/bash
echo "Enter a filename:"
read filename
case $filename in
# 匹配以 .txt 结尾的文件
*.txt)
echo "This is a text file."
;;
# 匹配以 .jpg 或 .jpeg 结尾的文件
(*.jpg | *.jpeg)
echo "This is a JPEG image."
;;
# 匹配以 .png 结尾的文件
*.png)
echo "This is a PNG image."
;;
# 匹配以单个字符作为前缀,且以 .log 结尾的文件
?.log)
echo "This is a log file with a single character prefix."
;;
# 匹配以 file 和一个数字作为前缀,且以 .dat 结尾的文件
file[0-9].dat)
echo "This is a data file with a digit in its name."
;;
# 默认匹配
*)
echo "Unknown file type."
;;
esac
5.1.3 for
for variable in list
do
commands
done
#variable:用于迭代的变量,可以是用户定义的任何有效变量名
#list:用于指定迭代的元素列表,可以是空格分隔的单词、数组的元素、命令输出结果等。
#例子一
#!/bin/bash
for fruit in apple banana cherry
do
echo "I like $fruit"
done
#例子二
#!/bin/bash
fruits=("apple" "banana" "cherry")
for fruit in "${fruits[@]}"
do
echo "I like $fruit"
done
例子三
#!/bin/bash
for file in $(ls *.txt)
do
echo "Processing file: $file"
# 执行其他操作
done
例子四
#!/bin/bash
for ((i = 1; i <= 5; i++))
do
echo "Count: $i"
done
例子五
#!/bin/bash
for i in {1..5}
do
echo "Number: $i"
done
5.1.4 while
while condition # condition 可以使用 [ ] 、[[ ]] 、命令
do
commands
done
# condition :用于判断真假的表达式或命令,只要 condition 的返回值为 true(即退出状态码为 0),则# # 循环内的 commands 将会被执行。
#commands : condition 为 true 时要执行的命令序列
#例子一
#!/bin/bash
count=1
while [ $count -le 5 ]
do
echo "Count: $count"
((count++))
done
#例子二
#!/bin/bash
file="file.txt"
while IFS= read -r line || [[ -n $line ]];
do
echo "Line: $line"
done < "$file"
#例子三
#!/bin/bash
while true
do
echo "Enter 'quit' to exit: "
read input
if [ "$input" = "quit" ]; then
break
fi
done
#例子四
#!/bin/bash
cat "$file" | while IFS= read -r line
do
echo "Line: $line"
done
6、算术运算
6.1 基本运算符
Shell支持以下基本的算术运算符:
+
:加法-
:减法*
:乘法/
:除法(整数除法)%
:取余**
:幂运算(仅在$(( ))
中支持)
$(( ))是一种用于进行算术运算的内置语法。它支持基本的算术运算符,并且可以嵌套使用。
#!/bin/bash
# 定义变量
num1=10
num2=5
# 加法
sum=$((num1 + num2))
echo "Sum: $sum" # 输出:Sum: 15
# 减法
difference=$((num1 - num2))
echo "Difference: $difference" # 输出:Difference: 5
# 乘法
product=$((num1 * num2))
echo "Product: $product" # 输出:Product: 50
# 除法
quotient=$((num1 / num2))
echo "Quotient: $quotient" # 输出:Quotient: 2
# 取余
remainder=$((num1 % num2))
echo "Remainder: $remainder" # 输出:Remainder: 0
# 幂运算
power=$((num1 ** num2))
echo "Power: $power" # 输出:Power: 100000
6.2 expr
expr是一个外部命令,用于执行算术和字符串运算,在使用expr的时候,运算符和操作数之间必须有空格,乘法运算需要转义(反斜杆)。
#!/bin/bash
# 定义变量
num1=10
num2=5
# 加法
expr 1 + 3 #直接输出4
sum=$(expr $num1 + $num2)
echo "Sum: $sum" # 输出:Sum: 15
# 减法
difference=$(expr $num1 - $num2)
echo "Difference: $difference" # 输出:Difference: 5
# 乘法
product=$(expr $num1 \* $num2)
echo "Product: $product" # 输出:Product: 50
# 除法
quotient=$(expr $num1 / $num2)
echo "Quotient: $quotient" # 输出:Quotient: 2
# 取余
remainder=$(expr $num1 % $num2)
echo "Remainder: $remainder" # 输出:Remainder: 0
6.3 let
let
命令用于执行算术运算,可以直接在变量上进行运算。使用let
命令时,不需要加上$
符号来引用变量。
#!/bin/bash
# 定义变量
num1=10
num2=5
# 加法
let sum=num1+num2
echo "Sum: $sum" # 输出:Sum: 15
# 减法
let difference=num1-num2
echo "Difference: $difference" # 输出:Difference: 5
# 乘法
let product=num1*num2
echo "Product: $product" # 输出:Product: 50
# 除法
let quotient=num1/num2
echo "Quotient: $quotient" # 输出:Quotient: 2
# 取余
let remainder=num1%num2
echo "Remainder: $remainder" # 输出:Remainder: 0
6.4 bc
bc
是一个支持浮点运算的计算器语言,可以用于更复杂的算术运算。通过管道将表达式传递给bc
进行计算。
#!/bin/bash
# 定义变量
num1=10.5
num2=5.3
# 加法
sum=$(echo "$num1 + $num2" | bc)
echo "Sum: $sum" # 输出:Sum: 15.8
# 减法
difference=$(echo "$num1 - $num2" | bc)
echo "Difference: $difference" # 输出:Difference: 5.2
# 乘法
product=$(echo "$num1 * $num2" | bc)
echo "Product: $product" # 输出:Product: 55.65
# 除法
quotient=$(echo "scale=2; $num1 / $num2" | bc) # scale=2设置小数点后2位
echo "Quotient: $quotient" # 输出:Quotient: 1.98
7、函数
注意事项:
- 函数定义必须在调用之前。
- 函数参数使用时不需要声明类型。
- 函数名遵循与变量相同的命名规则。
7.1 定义
function_name () {
# 函数体(命令序列)
}
# 例子一
#!/bin/bash
printStr () {
echo "hello world!"
}
printStr #执行后打印hello world!
7.2 函数参数
Shell 函数可以接受参数,与脚本的命令行参数类似。在函数内部,可以使用特殊变量 $1
, $2
, $3
等来访问传递给函数的参数。
#!/bin/bash
printStr () {
echo "hello $1 $2"
}
printStr world ! #执行后打印hello world !
7.3 局部变量
shell函数也支持使用局部变量,在函数内部使用 local
声明变量就可以将其变成局部变量。
#!/bin/bash
num=2
count_lines () {
local num=1 # 声明局部变量 num
echo "Inside function: ${num}"
}
# 调用函数
count_lines
echo "Outside function: ${num}"
7.4 函数返回值
shell 函数可以使用 return
命令来设置函数的退出状态码,这个退出状态码表示函数执行的结果。约定俗成的规定是,退出状态码为 0 表示成功,非 0 表示失败或者其他特定的错误码。
#!/bin/bash
validate_input () {
if [ $# -eq 0 ]; then
return 1 # 没有传入参数,返回 1 表示失败
fi
# 其他验证逻辑
return 0 # 验证通过,返回 0 表示成功
}
# 调用函数
validate_input 1
ret_code=$?
if [[ "$ret_code" -ne 0 ]]; then
echo "Validation failed."
else
echo "Validation succeeded."
fi
7.4 删除函数
unset -f function_name