shell脚本开发基础
什么是linux内置命令?什么是外置命令
内置命令:在系统启动时就加载入内存,常驻内存,执行效率更高,但是占用资源,cd
外置命令:系统需要从硬盘中读取程序文件,再读入内存加载
外置命令,也称之为,自已单独下载的文件系统命令,处于bash shell之外的程序
/bin
/usr/bin
/sbin
/usr/sbin
[root@linux home]# which cd
/usr/bin/cd
[root@linux home]#
通过1inux的type命令,验证是否是内置、外置命令
比如ps命令
[root@linux home]# type ps
ps is hashed (/usr/bin/ps)
[root@linux home]# type cd
cd is a shell builtin
[root@linux home]#
外置命令的特点是:一定会开启子进程执行
[root@linux home]# ps -ef --forest
root 8541 1083 0 17:16 ? 00:00:00 \_ sshd: root@pts/1
root 8548 8541 0 17:17 pts/1 00:00:00 | \_ -bash
root 8615 8548 0 18:09 pts/1 00:00:00 | \_ ps -ef --forest
子shell和父shell
为什么要学习子shell
she11的进程列表理念,需要使用()小括号,如下执行方式,就称之为,进程列表
source和.,执行脚本,只在当前的shel1环境中执行生效
指定bashsh解释器运行脚本,是开启subshell,开启子shel1运行脚本命令
./script,都会指定shebang,通过解释器运行,也是开启subshell运行命令
环境变量
环境变量一般指的是用export内置命令导出的变量,用于定义shell的运行环境、保证shell命令的正确执行。
shell通过环境变量确定登录的用户名、PATH路径、文件系统等各种应用。
环境变量可以在命令行中临时创建,但是用户退出shell终端,变量即丢失,如要永久生效,需要修改 环境变
量配置文件
- 用户个人配置文件/.bash_profile、/.bashrc 远程登录用户特有文件
- 全局配置文件/etc/profile、/etc/bashrc,且系统建议最好创建在/etc/profile.d/,而非直接修改主文件,修改全局配置文件,影响所有登录系统的用户
检查系统环境变量的命令
- set,输出所有变量,包括全局变量、局部变量
- env,只显示全局变量
- declare,输出所有的变量,如同set
- export,显示和设置环境变量值
撤销环境变量
- unset变量名,删除变量或函数。
设置只读变量
- readonly,只有shell结束,只读变量失效
环境变量文件的加载顺序
特殊状态变量
shell的特殊变量,用在如脚本,函数传递参数使用,有如下特殊的,位置参数变量
$0 获取she11脚本文件名,以及脚本路径
$n 获取she11脚本的第n个参数,n在1~9之间,如$1,$2, 9 ,大于 9 则需要写, 9,大于9则需要写, 9,大于9则需要写,{10},参数空格隔开
$# 获取执行的she11脚本后面的参数总个数
∗ 获取 s h e l 1 脚本所有参数,不加引号等同于 * 获取shel1脚本所有参数,不加引号等同于 ∗获取shel1脚本所有参数,不加引号等同于@作用,加上引号“$*“作用是接收所有参数为单个字符串,“$1$2…
$@ 不加引号,效果同上,加引号,是接收所有参数为独立字符串,如"$1"“$2"
[root@linux home]# cat ppp.sh
#filename:ppp.sh
#author:ysy
#created time:2024-05-26
#!/bin/bash
echo '################'
echo '$0实战'
echo ‘结果:’ $0
echo '################'
echo '$1实战'
echo ‘结果:’ $1
echo '################'
echo '$2实战'
echo ‘结果:’ $2
echo '################'
echo '$#实战'
echo ‘结果:’ $#
echo '################'
echo '$*实战'
echo ‘结果:’ $*
echo '################'
echo '$@实战'
echo ‘结果:’ $@
[root@linux home]#
[root@linux home]#
[root@linux home]# bash ppp.sh name 111 222 333 444
################
$0实战
‘结果:’ ppp.sh
################
$1实战
‘结果:’ name
################
$2实战
‘结果:’ 111
################
$#实战
‘结果:’ 5
################
$*实战
‘结果:’ name 111 222 333 444
################
$@实战
‘结果:’ name 111 222 333 444
[root@linux home]#
总结提高
$*和$的区别你了解吗?
$*和$@都表示传递给函数或脚本的所有参数
当 $* 梨$@不被双引号”“包围时,它们之间没有任何区别,都是将接收到的每个参数看做一份数据,彼此之间以空格来分隔。
但是当它们被双引号”“包含时,就会有区别了:
“$*“会将所有的参数从整体上看做一份数据,而不是把每个参数都看做一份数据。
"name 111 222 333 444"
"$@“仍然将每个参数都看作一份数据,彼此之间是独立的。
"name"
"111"
"222"
"333"
"444"
比如传递了 5 个参数,那么对于“$*“来说,这 5 个参数会合并到一起形成一份数据,它们之间是无法分割的;而对于“$@“来说,这 5 个参数是相互独立的,它们是 5 份数据。
如果使用 echo 直接输出“$*"和"$@“做对比,是看不出区别的;但如果使用 for 循环来逐个输出数据,立即就能看出区别来。
- $? 上一次命令执行状态返回值,0正确,非失败
- $$ 当前shel1脚本的进程号
- $! 上一次后台进程的PID
- $_ 获取上次执行的命令的最后一个参数
- 查找方式 man bash -> 搜索Special Parameters
[root@linux home]# cat ttt.sh
#!/bin/bash
# $#获取参数个数
[ $# -ne 2 ] && {
echo "must be two args"
exit 119 #终止程序运行,且返回119状态码,提供给当前shel1的$?变量,若是在函数里 可以return 119用法
}
echo "没毛病,就是2个参数"
[root@linux home]#
[root@linux home]# bash ttt.sh
must be two args
[root@linux home]# bash ttt.sh name 111 222
must be two args
[root@linux home]# echo $?
119
[root@linux home]# bash ttt.sh name 111
没毛病,就是2个参数
[root@linux home]# echo $?
0
[root@linux home]#
$!
获取上一次后台执行的程序的PID
[root@linux home]# nohup ping www.baidu.com & 1> /dev/null
[1] 8781
[root@linux home]# nohup: ignoring input and appending output to ‘nohup.out’
[root@linux home]# ps -elf | grep ping
4 S root 8781 8694 0 80 0 - 37523 poll_s 20:23 pts/0 00:00:00 ping www.baidu.com
0 R root 8783 8694 0 80 0 - 28203 - 20:24 pts/0 00:00:00 grep --color=auto ping
[root@linux home]# echo $!
8781
[root@linux home]#
$$
获取当前shell脚本的进程ID
[root@linux home]# cat ttt.sh
#!/bin/bash
# $#获取参数个数
[ $# -ne 2 ] && {
echo " must be two args "
exit 119 #终止程序运行,且返回119状态码,提供给当前shel1的$?变量,若是在函数里 可以return 119用法
}
echo "没毛病,就是2个参数"
echo "当前脚本的PID:$$"
[root@linux home]#
[root@linux home]# bash ttt.sh name 111
没毛病,就是2个参数
当前脚本的PID:8790
[root@linux home]#
$_
获取上次执行的命令的最后一个参数
[root@linux home]# bash ttt.sh name 111
没毛病,就是2个参数
当前脚本的PID:8790
[root@linux home]# echo $_
111
[root@linux home]#
bash的一些基础内置命令
- echo 输出变量内容
- eval 执行多个命令
- exec 不创建子进程执行后续命令,且执行完毕后,自动exit
- export
- read
- shift
echo 命令输出变量内容
-n 不换行输出
-e解析字符串中的特殊符号
\n 换行
\r回车
\t 制表符 四个空格
\b 退格
[root@linux shell_program]#
[root@linux shell_program]# echo 你真胖;echo你还挺可爱
你真胖
你还挺可爱
不换行打印
[root@linux shell_program]# echo -n 你真胖;echo你还挺可爱
你真胖你还挺可爱
[root@chaogelinux shell_program]# echo -n 你真胖;echo -n 你还挺可爱
你真胖你还挺可爱[root@chaogelinux shell_program]#
[root@chaogelinux shell_program]# echo -e "我看你挺\n好的"
我看你挺
好的
eval
执行多个命令
[root@linux home~]# eval cd /root;ls
anaconda-ks.cfg cmatrix Term-Animation-2.4 Term-Animation-2.4.tar.gz
[root@linux ~]#
exec
不创建子进程执行后续命令,且执行完毕后,自动exit
[root@linux ~]# ps -ef --forest
root 8688 1083 0 19:41 ? 00:00:00 \_ sshd: root@pts/0
root 8694 8688 0 19:42 pts/0 00:00:00 | \_ -bash
root 8720 8694 0 19:42 pts/0 00:00:00 | \_ bash
root 8731 8720 0 19:42 pts/0 00:00:00 | \_ ps -ef --forest
[root@linux ~]# exec date
Sun May 26 19:43:07 CST 2024
[root@linux ~]#
[root@linux ~]# ps -ef --forest
root 8688 1083 0 19:41 ? 00:00:00 \_ sshd: root@pts/0
root 8694 8688 0 19:42 pts/0 00:00:00 | \_ -bash
root 8732 8694 0 19:43 pts/0 00:00:00 | \_ ps -ef --forest
字符串截取
# 从开头删除匹配最短
## 从开头删除匹配最长
% 从结尾削除匹配最短
%% 从结尾删除匹配最长
#指定字符内容截取
a*c 匹配开头为a,中间任意个字符,结尾为c的字符串
a*c 匹配开头为a,中间任意个字符,结尾为C的字符串
#语法
name“yuchao” # 该变量的值,有索引,分别是从e,1,2,3,4开始
$(变量) 返回变量值
$(#name) 返回变量长度,字符长度-
$(变量:start) 返回变量start数值之后的学符,且包含start的数字
$(变量:start:length) 提取start之后的length限制的字符,例如$(name:4:1)
$(变量#word) 从变量开头删除最短匹配的word子串$(name:ysy)
$(变量##word) 从变量开头,删除最长匹配的word
$(变量%word) 从变量结尾刷除最短的word
$(变量%%word) 从变量结尾开始制除最长匹配的word
替换
$(变量/pattern/string) 用string代替第一个配的pattern
$(变量//pattern/string) 用string代替所有的pattern
实战:批量修改所有文件名
现有9个txt文件,需要将文件名称中的test删除
[root@linux test]# touch name-{1..9}-test.txt
[root@linux test]# ll
total 0
-rw-r--r--. 1 root root 0 May 26 18:42 name-1-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-2-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-3-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-4-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-5-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-6-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-7-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-8-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-9-test.txt
[root@linux test]#
思路:
1.单个文件怎么去除? 使用mv命令直接修改
[root@linux test]# mv name-1-test.txt name-1.txt
[root@linux test]# ll
total 0
-rw-r--r--. 1 root root 0 May 26 18:42 name-1.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-2-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-3-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-4-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-5-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-6-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-7-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-8-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-9-test.txt
[root@linux test]#
2.利用变量的子串功能,去掉字符信息
[root@linux test]# filename=name-2-test.txt
[root@linux test]# echo $filename
name-2-test.txt
[root@linux test]# echo ${filename}
name-2-test.txt
[root@linux test]# echo ${filename//-test/}
name-2.txt #可以得到最中的文件名,然后配合mv命令进行修改,如第3步
[root@linux test]#
3.利用反引号功能,修改单个文件名
[root@linux test]# echo ${filename//-test/}
name-2.txt
[root@linux test]# mv ${filename} `echo ${filename//-test/}`
[root@linux test]# ll
total 0
-rw-r--r--. 1 root root 0 May 26 18:42 name-1.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-2.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-3-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-4-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-5-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-6-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-7-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-8-test.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-9-test.txt
[root@linux test]#
4.使用循环批量修改
[root@linux test]# #设置循环变量
[root@linux test]# ls *-test.txt
name-3-test.txt name-4-test.txt name-5-test.txt name-6-test.txt name-7-test.txt name-8-test.txt name-9-test.txt
[root@linux test]# echo `ls *-test.txt`
name-3-test.txt name-4-test.txt name-5-test.txt name-6-test.txt name-7-test.txt name-8-test.txt name-9-test.txt
[root@linux test]# for filename in `ls *-test.txt` ;do echo $filename;done
name-3-test.txt
name-4-test.txt
name-5-test.txt
name-6-test.txt
name-7-test.txt
name-8-test.txt
name-9-test.txt
[root@linux test]# for filename in `ls *-test.txt` ;do mv $filename `echo ${filename//-test/}`;done
[root@linux test]# ll
total 0
-rw-r--r--. 1 root root 0 May 26 18:42 name-1.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-2.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-3.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-4.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-5.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-6.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-7.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-8.txt
-rw-r--r--. 1 root root 0 May 26 18:42 name-9.txt
[root@linux test]#
特殊shell扩展变量
这四个扩展变量,都是对变量的值进行判断、处理
:-
如果parameter变量值为空,返回word字符串,赋值给result变量
result=$(parameter:-word)
:=
如果para变量为空,则word替代变量值,且返回其值
$(parameter:=word)
:?
如果para变量为空,word当作stderr输出,否则输出变量值
用于设置变量为空导致错误时,返回的错误信息
$(parameter:?word
:+
如果para变量为空,什么都不做,否则word返回
$(parameter:+word)
:-
- 判断变量值是否为空,如果为空就返回后面的字符信息
- 如果不为空,就返回本身
#name为空
[root@linux test]# echo $name
[root@linux test]# result=${name:-hello}
[root@linux test]# echo $name
[root@linux test]# echo $result
hello
[root@linux test]#
#name不为空
[root@linux test]# result=${name:-hello}
[root@linux test]#
[root@linux test]# echo $name
123
[root@linux test]# echo $result
123
[root@linux test]#
:=
- 判断变量值是否为空,如果为空就返回后面的字符信息,同时将后边的信息赋给变量本身
- 如果不为空,就返回变量内容
#name为空
[root@linux test]# echo $name $result
[root@linux test]# result=${name:=hahahahha}
[root@linux test]# echo $result
hahahahha
[root@linux test]# echo $name
hahahahha
[root@linux test]#
#name不为空
[root@linux test]# name="150"
[root@linux test]# result=${name:=hahahahha}
[root@linux test]#
[root@linux test]# echo $result
150
[root@linux test]# echo $name
150
[root@linux test]#
:?
- 当变量值为空,出动抛出错误信息
- 不为空,返回变量的信息
[root@linux test]# echo ${nname}
[root@linux test]# echo ${nname:?变量为空}
-bash: nname: 变量为空
[root@linux test]# nname="yyyyy"
[root@linux test]# echo ${nname:?变量为空}
yyyyy
[root@linux test]#
:+
- 当变量为空,什么都不做
- 不为空,返回后面的信息
[root@linux test]# echo $tom
[root@linux test]# echo ${tom:+6666}
[root@linux test]# tom="abc"
[root@linux test]# echo $tom
abc
[root@linux test]# echo ${tom:+6666}
6666
[root@linux test]#
实战:删除过期的数据备份文件脚本
删除7天以上的过期数据
find xargs搜索,且删除
#删除7天以上的过期数据
find需要搜索的目录 -name 你要搜索的文件名字 -type文件类型 -mtime +7| xargs rm -f
cat del data.sh
#shel1语法是否有bug
#希望删除某个数据文件夹的备份文件
#dir_path="/data/mysql_back_data/"
#如果有bug歧义,就会在当前目录,搜索,删除
#变量扩展的改进
find $(dir_path:=/data/mysql back_data/) -name '*.tar.gz' -type f -mtime +7|xargs rm -f
- 当变量为空,什么都不做
- 不为空,返回后面的信息
[root@linux test]# echo $tom
[root@linux test]# echo ${tom:+6666}
[root@linux test]# tom="abc"
[root@linux test]# echo $tom
abc
[root@linux test]# echo ${tom:+6666}
6666
[root@linux test]#
实战:删除过期的数据备份文件脚本
删除7天以上的过期数据
find xargs搜索,且删除
#删除7天以上的过期数据
find需要搜索的目录 -name 你要搜索的文件名字 -type文件类型 -mtime +7| xargs rm -f
cat del data.sh
#shel1语法是否有bug
#希望删除某个数据文件夹的备份文件
#dir_path="/data/mysql_back_data/"
#如果有bug歧义,就会在当前目录,搜索,删除
#变量扩展的改进
find $(dir_path:=/data/mysql back_data/) -name '*.tar.gz' -type f -mtime +7|xargs rm -f
日常学习记录