前言:咱们接着上面的文章来继续介绍shell脚本语法,整体来看语法量还是挺大的。。。
9,测试运算
(1)test 和 [
- test 是一种早期的命令形式,等价于 [,它们在功能上几乎完全相同。可以理解为 [ 就是test命令的别名;
- [ 是现在常用的一种用于进行条件测试的命令,它的语句结构比test更加简洁和容易阅读
- [ ] 的左右方括号 [ 和 ] 必须用空格与表达式隔开。
- [ 执行测试操作,而 ] 仅作为语法符号来表示条件表达式的结束位置。
- 返回 0 表示条件为真(True)
- 返回 1 表示条件为假(False)
(2)test指令的应用分类
- 文件相关测试:用于检查文件的属性、类型或权限
- 字符串相关测试:用于比较字符串是否相等、非空等
- 整数比较:用于比较两个整数
- 逻辑运算:可以组合多个条件进行测试
(3)举例
# 文件存在性检查
if [ -e /etc/passwd ]; then
echo "文件存在"
else
echo "文件不存在"
fi
# 字符串非空检查
if [ -e /etc/passwd ]; then
echo "文件存在"
else
echo "文件不存在"
fi
# 整数比较
if [ -n "$USER" ]; then
echo "当前用户是 $USER"
else
echo "用户未定义"
fi
# 逻辑运算
if [ -d /home -a -w /home/user ]; then
echo "/home 存在并且用户目录可写"
else
echo "/home 不存在或用户目录不可写"
fi
10 ,语句结构
10.1 分支语句
- if 分支语句
############
#单分支
if 条件; then
Command;
fi
##############
#双分支
if 条件; then
Command;
else
Command;
fi
##############
#多分支
if 条件; then
Command;
elif 条件; then
Command;
else
Command;
fi
##############
#举例
if [ $? -eq 0 ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') 打包成功:person_data-${Date}.tar.gz" | tee -a ${LogFile}
else
echo "$(date '+%Y-%m-%d %H:%M:%S') 打包失败,请检查密码、网络是否正常" | tee -a ${LogFile}
exit 1
fi
- case 分支语句
1, * :匹配任意长度的任意字符示例:*.txt 匹配任何以 .txt 结尾的字符串,如 file.txt 和 my_data.txt。2,? :匹配任意单个字符示例:file?.txt 匹配 file1.txt 或 fileA.txt,但不匹配 file12.txt。3,[...] :匹配方括号内的任意一个字符示例:file[abc].txt 匹配 filea.txt、fileb.txt、filec.txt。也可以使用字符范围:file[a-z].txt 匹配 filea.txt 到 filez.txt。4,| :多个模式用管道分隔,表示“或”示例:*.jpg | *.png 匹配以 .jpg 或 .png 结尾的字符串。5,转义字符 \:用于匹配特殊字符示例:\*.txt 匹配文件名为 *.txt 的文件,而不是匹配所有以 .txt 结尾的文件。6,) :结束当前模式每个分支的模式必须以 ) 结束。
case 变量 in
模式1)
# 当变量匹配模式1时执行的代码
;;
模式2)
# 当变量匹配模式2时执行的代码
;;
*)
# 以上模式都不匹配时执行的代码
;;
esac
#####################################
#举例
#!/bin/bash
read -p "请输入一个字母: " char
case $char in
[a-z])
echo "你输入的是小写字母"
;;
[A-Z])
echo "你输入的是大写字母"
;;
[0-9])
echo "你输入的是数字"
;;
*)
echo "输入的不是字母或数字"
;;
esac
10.2 循环语句
- for 循环语句
# 普通风格
for 变量名 in 参数列表
do
command;
done
# c语言风格
for ((i=0; i<=5; i++))
do
command
done
#######################################
#打印99乘法表
#!/bin/bash
for (( i=1; i<=9; i++ )) ;do
for (( j=1; j<=i; j++ ));do
echo -ne "$i * $j = $(expr $i \* $j) \t"
if [ $i -eq $j ];then
echo
fi
done
done
- while 循环语句
while 条件; do
command;
done
#######################
#循环语句举例
#!/bin/bash
counter=1
while [ $counter -le 5 ]; do
echo "计数器:$counter"
((counter++)) # 计数器加 1
done
11,shell中的括号的作用的总结
11.1 大括号
- 变量分隔
ubuntu@yyy:~/test$ name=TOM
ubuntu@yyy:~/test$ echo ${name}
TOM
# 无输出
ubuntu@yyy:~/test$ echo $namefdas
- 命令块
- 字符串组合
echo {a,b,c} 会输出 a b c
- 生成有序序列
echo {1..5} 会输出 1 2 3 4 5。
11.2 小括号
- 命令组合
- 启用子shell
- 进程替换
- another_command 的输出被视为一个临时文件或命名管道的内容。
- command 可以从这个“文件”中读取数据
# 使用 diff 比较两个命令的输出
diff <(ls dir1) <(ls dir2)
# 使用 grep 过滤动态生成的文本
grep "pattern" <(echo -e "line1\npattern\nline3")
- command 的输出被重定向到一个临时文件或命名管道。
- another_command 从这个文件或管道中读取数据
# 将ls的输出传递给sort, 最终结果是对 ls 的输出进行排序
ls > >(sort)
11.3 中括号
- 条件测试
- 通配符
- 单字符匹配
[abc]:匹配任意一个字符,如 a、b 或 c
- 范围匹配
[a-z]:匹配小写字母 a 到 z 中的任意一个字符。
[0-9]:匹配数字 0 到 9 中的任意一个字符。
- 否定匹配
[^abc]:匹配除了 a、b、c 以外的任何字符。^ 放在 [] 中的开头表示取反。
12,Shell 中的引号
12.1 双引号
使用双引号括起来的字符串,变量值中的命令和变量会替换为输出值,其他字符串原样输出;
# 变量会替换为具体的变量值
ubuntu@yyy:~$ name=TOM
ubuntu@yyy:~$ test="name is ${name}"
ubuntu@yyy:~$ echo $test
name is TOM
12.2 单引号
使用单引号括起来的字符串,变量值中的内容会完全保留字面值,所有的字符都不会被解释或扩展 ;
ubuntu@yyy:~/test$ test='name is ${name}'
ubuntu@yyy:~/test$ echo $test
name is ${name}
11.3 反引号
反引号的作用等同于 $(),会将括起来的内容当作命令执行,并将其输出作为一个字符串返回;
ubuntu@yyy:~/test$ filename=$(ls)
ubuntu@yyy:~/test$ echo $filename
file1 file2 file3 file4 file5 file6
13,管道符使用
Linux 相关操作系统中,使用 | 符号来表示一个匿名管道符,从而实现将前一个语句或命令的标准输出(stdout)作为输入(stdin)传递给下一个语句或命令
语法:语句1 | 语句2
13.1 单个命令的使用场景
ps aux | grep "apache"
13.2 复杂命令组合(语句)的场景
# 将 for语句本来应该输出到屏幕的内容作为sort命令的输出,然后将sort命令本该输出到屏幕的内容作为while语句的输入
for key_name in "${!key_arry[@]}"; do
echo "${key_arry[$key_name]} $key_name"
done | sort -nr | while read size key; do
echo "$size $key"
done
13.3 不能直接使用管道符处理方法
存在某些命令不能直接使用管道符来处理从上一个命令传递的数据,例如:rm、mv、cp、kaill等。可以通过 xargs 来实现管道操作。xargs 可以将管道传递的输入转换为命令的参数,使得这些命令能够处理数据流
格式:语句1 | xargs 命令或语句
xargs 会自动将多行输入转换为单行参数传递给命令
14, 使用 heredoc
heredoc 是一种特殊的重定向方式,允许一次性从标准输入读取多行内容给一个命令。有两种语法格式
14.1 定界符无引号
语法:
# <<DELIMITER 是固定搭配
# DELIMITER-----定界符
command <<DELIMITER
line_1
line_2
...
DELIMITER
举例:
################################
# 将多行文本直接传递给 cat 命令进行输出
################################
#!/bin/bash
cat <<EOF
这是一个多行字符串。
Heredoc 示例演示。
EOF
# 输出结果
#=======================
这是一个多行字符串。
Heredoc 示例演示。
#=======================
################################
# 将 Heredoc 的内容保存到文件中
################################
#!/bin/bash
cat <<EOF > output.txt
这是保存到文件的内容。
包含多行文本数据。
EOF
# 输出结果
#=======================
#执行后会创建一个output.txt 文件,内容如下:
这是一个多行字符串。
Heredoc 示例演示。
#=======================
################################
# 默认情况下,Heredoc 中的变量会被替换
################################
#!/bin/bash
name="小明"
cat <<EOF
你好,$name!
欢迎来到 Shell 脚本学习。
EOF
# 输出结果
#=======================
你好,小明!
欢迎来到 Shell 脚本学习。
#=======================
14.2 定界符有引号
定界符加上单引号或双引号都可以;内容中的变量、通配符、转义字符都不会被Shell解释,而是将内容视为纯文本,不会进行任何拓展
语法:
command <<'DELIMITER'
line_1
line_2
...
DELIMITER
举例:
################################
# 使用单引号或 <<'DELIMITER' 来禁止变量替换
################################
#!/bin/bash
name="小明"
cat <<'EOF'
你好,$name!
这是不替换变量的示例。
EOF
15,重定向
Linux 中的三个文件描述符 0 1 2 固定和标准输入(stdin)、标准输出(stdout)和标准错误(stderr)进行绑定。当某个进程启动时,操作系统会自动将它的标准输入(stdin)、标准输出(stdout)和标准错误(stderr)绑定到对应的终端设备上
- 标准输入:文件描述符:0 文件路径:/dev/stdin 符号表示:< 或 0
- 标准输出:文件描述符:1 文件路径:/dev/stdout 符号表示:> 或 >1
- 标准输入:文件描述符:2 文件路径:/dev/stderr 符号表示: 2>
ubuntu@ubuntu:~$ ls -l /dev/std*
lrwxrwxrwx 1 root root 15 10月 8 15:31 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 10月 8 15:31 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 10月 8 15:31 /dev/stdout -> /proc/self/fd/1
15.1 输入重定向
wc -l < CheckEncryption.log
15.2 输出重定向
# 追加内容,会覆盖以前内容
# 如果文件不存在,会先创建对应的文件,如果文件存在且存在内容,会将以前的内容全部清空
echo "12345" > 1.txt
15.3 错误重定向
# 同时将标准输出和标准错误都输出到不同文件中
output 1> file1 2> file2
# 将标准输出和标准错误都输出到同一文件中
output 1> file1 2> file1
# &符号的作用是表示1是描述符不是文件名1
output 1> file1 2>&1
# 简写形式 &> 或 >& 等价
outpu &> file1
# 错误形式。因为此时标准输出也不知道自己应该重定向到哪个文件,故会将内容输出到屏幕
output 2>&1 > file.txt
16,环境变量管理
15.1 PATH 变量简介
- PATH变量定义了可执行文件的路径,当以相对路径执行某个命令时,会通过PATH变量定义的路径来进行查找;
- 通过PATH定义的路径进行查找时,不会递归查找,只会查找所指定路径下是否存在该可执行文件
- Linux中的PATH变量是一个全局变量,对所有用户和进程可见,所以系统启动时需要首先加载PATH变量,这样在才能在当前或后续的子进程中使用PATH变量
16.2 查看PATH变量值
ubuntu@ubuntu:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/xtrabackup-8.0.35-31/bin
16.3 临时修改PATH变量
# 这条命令的作用是将 /directory 添加到当前终端的 PATH 环境变量中
export PATH=$PATH:/directory
- 在前面添加路径
# 解释:将 /my/custom/path 添加到 PATH 变量的前面。 # 结果:系统会优先搜索 /my/custom/path 目录下的可执行文件 export PATH=/my/custom/path:$PATH
- 在后面添加路径
# 解释:将 /my/custom/path 添加到 PATH 变量的后面。
# 结果:系统会先搜索原有的路径,再搜索 /my/custom/path 目录下的可执行文件
export PATH=$PATH:/my/custom/path
16.4 永久修改PATH变量
方式一:通过在当前Shell的配置文件中进行定义,后续启动Shell进程的时候加载配置文件的同时,也会使得定义的PATH变量生效;
用户级配置文件:仅在当前用户下生效,对其他用户无影响。例如:PATH 修改添加到 ~/.bashrc、~/.bash_profile
系统级配置文件:所有用户在启动 Shell 进程时都会加载这些配置文件。例如/etc/profile 或 /etc/profile.d/
方式二:通过在/etc/environment 这个配置文件中定义PATH变量,系统初始化时会读取该文件中的内容,使其对所有用户生效
16.5 不受PATH影响的命令
# 例如sudo、crontab等命令具有自己的PATH变量,默认情况下不会继承用户的 PATH
/etc/crontab
crontab:
可以通过修改配置文件中的PATH路径来自定义crontab的PATH变量
/etc/sudoers
sudo
可以通过修改 Defaults secure_path 来修改sudo的PATH变量,或者注释掉该选项,就会默认使用当前用户的PATH
[Service]
Environment="PATH=/custom/path:/usr/bin:/bin"
systemd也拥有自己的PATH变量,不会继承用户的 PATH;可以通过在service文件中添加Environment 指令来指定PATH路径
16.6 env 命令
env是用于查看、临时设置和管理环境变量。它可以显示当前会话中所有可用的环境变量,并且允许在临时环境中设置变量后再运行命令
# 查看当前所有的环境变量
env
# 临时设置一个或多个环境变量,并运行指定的命令
env variable=value command
16.7 其他变量
$$ :获取当前进程的pid; $PPID :获取当前进程的父进程 pid
17,子Shell 和 子进程
17.1 子Shell
在Shell 中通过将某些命令在小括号中执行,就会启动一个子shell来执行这个命令。子 Shell 在启动时会完全继承当前Shell的相关环境信息,例如局部变量、环境变量等
ubuntu@yyy:~$ name=tom
ubuntu@yyy:~$ (echo $name) tom
17.2 子进程
#使用bash这种解释器时,可以通过bash -c 来创建一个子进程,此时子进程只继承父进程的 环境变量,而不继承局部变量
##########################################
ubuntu@ubuntu:~$ name=tom
ubuntu@ubuntu:~$ export sex=m
# 不会继承定义的局部变量
ubuntu@ubuntu:~$ bash -c 'echo $name'
ubuntu@ubuntu:~$ bash -c 'echo $sex'
m