文章目录
Shell
bash
#shell会话可以存储用户执行的命令,使用history查看
[root@master ~]# history | tail -n 10
101 setenforce 0
102 etenforce
103 getenforce
104 ping www.baidu.com
105 shutdown now
106 vim /etc/my.cnf
107 cat /etc/my.cnf
108 history
109 history | less
110 history | tail -n 10
#变量HISTFILE是指存放历史命令的文本文件路径
[root@master ~]# echo $HISTFILE
/root/.bash_history
[root@master ~]# tail -n 10 /root/.bash_history
ss -anlt
setenforce 0
getenforce
yum -y install ncurses-compat-libs
mysql -uroot -p'sRgWy6inmT%2' -e "set password = password('sakura123');"
mysql -uroot -p'sRgWy6inmT%2' -e "set password = password('sakura123');" --connect-expired-password
mysql -uroot -psakura123
vim /etc/my.cnf
systemctl restart mysqld.service
mysql -uroot -psakura123
[root@master ~]#
#history命令
#一般使用-c和-r选项
-c //清空历史命令
-r [历史命令文件存放路径] //恢复历史命令
[root@master ~]# history -c
[root@master ~]# history
1 history
[root@master ~]# history -r ~/.bash_history
[root@master ~]# history | tail -n 5
160 mysql -uroot -psakura123
161 vim /etc/my.cnf
162 systemctl restart mysqld.service
163 mysql -uroot -psakura123
164 history | tail -n 5
[root@master ~]#
#调用历史命令
#![历史命令id]
[root@master ~]# history | tail -n 5
162 systemctl restart mysqld.service
163 mysql -uroot -psakura123
164 history | tail -n 5
165 echo "123"
166 history | tail -n 5
[root@master ~]# !165
echo "123"
123
[root@master ~]#
#!! //执行上一条命令
[root@master ~]# echo "123"
123
[root@master ~]# !!
echo "123"
123
[root@master ~]#
变量
变量只能以数字、字母、下划线开头,并且严格区分大小写
变量分为环境变量和局部变量(环境变量和局部变量在后面单独分类详细说明)
环境变量也称为全局变量,不管是父shell还是子shell,都可以使用,例如之前安装中间件时经常会用的$PATH设置环境变量,环境变量也分为“自定义”和“内置”两种
局部变量一般用于脚本或函数里
#使用等号给变量复制,引用时要加上$
[root@master ~]# name="tom"
[root@master ~]# echo $name
tom
[root@master ~]# echo name
name
[root@master ~]# echo ${name}
tom
[root@master ~]#
父shell和子shell
[root@master ~]# pstree
systemd─┬─NetworkManager───2*[{NetworkManager}]
├─VGAuthService
├─agetty
├─auditd───{auditd}
├─crond
├─dbus-daemon───{dbus-daemon}
├─polkitd───5*[{polkitd}]
├─rsyslogd───2*[{rsyslogd}]
├─sshd───sshd───sshd───bash───pstree //现在是在bash中,也就是父shell
├─sssd─┬─sssd_be
│ └─sssd_nss
├─systemd───(sd-pam)
├─systemd-journal
├─systemd-logind
├─systemd-udevd
├─tuned───4*[{tuned}]
└─vmtoolsd───3*[{vmtoolsd}]
[root@master ~]# bash
[root@master ~]# pstree
systemd─┬─NetworkManager───2*[{NetworkManager}]
├─VGAuthService
├─agetty
├─auditd───{auditd}
├─crond
├─dbus-daemon───{dbus-daemon}
├─polkitd───5*[{polkitd}]
├─rsyslogd───2*[{rsyslogd}]
├─sshd───sshd───sshd───bash───bash───pstree //输入bash,进入新的shell
├─sssd─┬─sssd_be
│ └─sssd_nss
├─systemd───(sd-pam)
├─systemd-journal
├─systemd-logind
├─systemd-udevd
├─tuned───4*[{tuned}]
└─vmtoolsd───3*[{vmtoolsd}]
[root@master ~]#
在父shell中定义的变量,如果切换到子shell,是不会生效的,同理在子shell中定义的变量也无法在父shell中生效
每次执行脚本时,都会调用sh或bash解释器,所以不会保留当前shell的变量
但是如果使用“source”或“.”+符号,就会在当前shell加载
[root@test ~]# vim 1.txt
[root@test ~]# cat 1.txt //向1文件中写入一个定义变量的语句
#!/bin/bash
name=jerry
[root@test ~]# name=tom //在当前shell中定义name为tom
[root@test ~]# bash 1.txt //使用bash开启一个子shell执行脚本
[root@test ~]# echo $name //打印name,还是tom,因为脚本执行是在子shell中,而不是父shell
tom
[root@test ~]# source 1.txt //使用source执行,在当前的shell中执行
[root@test ~]# echo $name //打印name,发现已经改变了,因为执行的是当前的shell
jerry
[root@test ~]#
引号
#单引号:无法识别特殊语法。只要是单引号中的内容,都会以字符串的形式处理,不会识别特殊符号和命令
[root@test ~]# name1=tom
[root@test ~]# echo $name1
tom
[root@test ~]# name2="${name1}" //双引号可以识别到定义的name1变量,并将name1的值赋给name2
[root@test ~]# echo $name2
tom
[root@test ~]#
#双引号:可以识别特殊语法。能识别特殊字符以及命令,如果想在双引号中不识别,可以使用“\”反斜杠进行转义
[root@test ~]# name3='${name1}' //同样是赋值操作,单引号就无法读取到name1的值
[root@test ~]# echo $name3
${name1}
[root@test ~]#
#反引号:识别命令,并将命令执行的结果传回给bash
[root@test ~]# name=`ls`
[root@test ~]# echo $name //将ls执行的结果赋值给name
1.txt anaconda-ks.cfg mysql-5.7.34-linux-glibc2.12-x86_64.tar.gz passwd
[root@test ~]#
变量
本地变量
定义shell变量时,变量名不需要加$
本地变量只在当前shell生效,退出或进入新shell时,变量失效
环境变量
环境变量一般指的是使用export导出的变量,用于定义shell的运行环境,保证shell命令的正确执行。
环境变量可以在命令行界面中临时创建,但是退出当前shell或登录子shell后就会失效,如果想永久生效,需要修改环境变量配置文件。PATH只是环境变量的一种
#针对当前用户生效的配置文件:
[root@test ~]# ls ~/.bash_profile
/root/.bash_profile
[root@test ~]#
#针对远程用户生效的配置文件:
[root@test ~]# ls ~/.bashrc
/root/.bashrc
[root@test ~]#
#全局生效的配置文件:
[root@test ~]# ls /etc/profile
/etc/profile
[root@test ~]#
读取变量配置文件顺序:先加载全局配置文件,在加载/.bashrc,最后加载/.bash_profile。如果三个文件中定义的变量相同,以最后的~/.bash_profile为准
检查系统环境变量的命令
#set:输出所有的变量,包括全局变量和局部变量
[root@test ~]# name=tom
[root@test ~]# echo $name
tom
[root@test ~]# set | grep ^name
name=tom
[root@test ~]#
#env:只显示全局变量
[root@test ~]# env | wc -l
29
[root@test ~]#
#输出所有变量,等同于set
[root@test ~]# declare | wc -l
1578
[root@test ~]# set | wc -l
1578
[root@test ~]#
显示和设置环境变量:export
#unset取消变量或函数
[root@test ~]# echo $name
tom
[root@test ~]# unset name
[root@test ~]# echo $name
[root@test ~]#
#readroly设置只读变量,当前shell失效时变量也失效
[root@test ~]# readonly password="123"
[root@test ~]# echo $password
123
[root@test ~]# password="123456" //重新设置password时会提示该变量为只读变量
-bash: password: readonly variable
[root@test ~]#
#export设置环境变量
[root@test ~]# export a=123
[root@test ~]# echo $a
123
[root@test ~]# export | wc -l //列出所有的环境变量
27
[root@test ~]#
特殊变量
shell的特殊变量,主要用于脚本、函数参数传递中
$0:文件名
$1、2、3...n:参数(大于10后写为${10},参数中间用空格隔开)
$#:统计参数的个数
$* 和 $@ :都表示传递给脚本的所有参数,当$* 和 $@不被双引号包围时,没有任何区别,当被双引号包围时,$*会将所有参数看成一个整体,而$@是将每一个参数看成整体,例如:
"$*" : "a,b,c,d"
"$@" : "a"
"b"
"c"
"d"
[root@test ~]# vim test1.sh
[root@test ~]# cat test1.sh
#! /bin/bash
echo '打印 $0 $1 $2 $3的结果'
echo $0 $1 $2 $3
echo '------------------'
echo '打印 $#的结果'
echo $#
echo '------------------'
echo '打印 $*的结果'
echo $*
echo '------------------'
echo '打印 $@的结果'
echo $@
[root@test ~]# bash test1.sh w x b
打印 $0 $1 $2 $3的结果
test1.sh w x b
------------------
打印 $#的结果
3
------------------
打印 $*的结果
w x b
------------------
打印 $@的结果
w x b
[root@test ~]#
特殊状态变量
$?:取出上一次命令的返回值,0为正确,非0错误
$$:当前shell的进程号
$!:上一次后台进程的PID
$_:取上一次命令的最后一个参数
示例:
// $?
[root@test ~]# ls passwd
passwd
[root@test ~]# echo $?
0
[root@test ~]# ls sadadas
ls: cannot access 'sadadas': No such file or directory
[root@test ~]# echo $? //查看不存在的文件时,报错,返回值为非0
2
[root@test ~]#
// $$
[root@test ~]# echo $$ //可以放到脚本中,获取到的就是当前脚本的pid
1558
[root@test ~]#
// $!
//先执行一个后台命令
[root@test ~]# nohup ping www.baidu.com & 1> /dev/null
[1] 1650
[root@test ~]# nohup: ignoring input and appending output to 'nohup.out'
[root@test ~]#
[root@test ~]# echo $!
1650
[root@test ~]# ps -aux | grep ping
root 1650 0.0 0.2 55256 4780 pts/0 S 19:47 0:00 ping www.baidu.com
root 1653 0.0 0.0 12140 1064 pts/0 R+ 19:49 0:00 grep --color=auto ping
[root@test ~]#
// $_
[root@test ~]# bash test1.sh w x b
打印 $0 $1 $2 $3的结果
test1.sh w x b
------------------
打印 $#的结果
3
------------------
打印 $*的结果
w x b
------------------
打印 $@的结果
w x b
[root@test ~]# echo $_ //取最后一个参数
b
[root@test ~]#
shell子串
bash内置命令
内置命令在所有的linux发行版里都能够识别
echo
eval
exec
export
read
shift
echo:
-n:不换行输出
-e:解析字符穿中的特殊符号
#特殊字符:
\n:换行
\r:回车
\t:制表符(四个空格)
\b:退格
#ecoh命令自带换行,输出多个echo可以用分号连接
[root@test ~]# echo "今天是星期一" ; echo "是雨天"
今天是星期一
是雨天
[root@test ~]#
#加上-n就不会换行
[root@test ~]# echo -n "今天是星期一" ; echo "是雨天"
今天是星期一是雨天
#echo没加-e选项时不会识别特殊字符
[root@test ~]# echo "今天是星期一\n是雨天"
今天是星期一\n是雨天
[root@test ~]# echo -e "今天是星期一\n是雨天"
今天是星期一
是雨天
[root@test ~]#
#除了echo打印,还有printf打印,printf会自动识别特殊字符
[root@test ~]# printf "今天是星期一\n是雨天\n"
今天是星期一
是雨天
[root@test ~]#
eval:
#执行多个命令,以分号隔开
[root@test ~]# eval echo "132" ; pwd
132
/root
[root@test ~]#
exec
#执行完命令后自动exit退出
[root@test ~]# su - tom //切换到tom用户
Last login: Mon Mar 20 08:14:11 CST 2023 on pts/0
[tom@test ~]$ exec ls /tmp/ //执行ls后退出
vmware-root_928-2731217671 vmware-root_932-2722632322 vmware-root_941-4022177618 vmware-root_954-2722108059
vmware-root_929-3980167385 vmware-root_938-2689078411 vmware-root_943-4013723344
vmware-root_930-2722763397 vmware-root_939-4022308693 vmware-root_951-4013330126
[root@test ~]# //回到了root用户
子串语法
${变量} :返回变量值
${#变量} :返回变量长度(字符长度)
${变量:start} :(返回变量start之后的值,后面有具体案例便于理解)
${变量:start:length} :(返回变量start之后的值,但是受限制于length)
${变量#test} :(从变量开头删除最短匹配的test字串)
${变量##test} :(从变量开头删除最长的test字串)
${变量%test} :(从结尾删除最短的test)
${变量%%test} :(从结尾删除最长的test)
${变量/test/test1} :(将匹配到的第一个test替换为test1)
${变量//test/test1} :(将匹配到的所有test替换为test1)
子串实际案例
#shell变量截取字符串通常有两种方式:从指定位置开始截取 和 从指定字符(子字符串)开始截取
[root@test ~]# name=sakura
[root@test ~]# echo $name
sakura
#输出变量
[root@test ~]# echo ${name}
sakura
#统计变量的长度(字符长度)
[root@test ~]# echo ${#name}
6
#根据索引截取start后的字符(start是指数字,索引是从0开始算,例如这里的sakura索引就是012345)
[root@test ~]# echo ${name:2}
kura
#根据索引截取字符串并指定长度
[root@test ~]# echo ${name:2:1}
k
#删除子串
[root@test ~]# name2="sakura sakura sakura"
[root@test ~]# echo ${name2}
sakura sakura sakura
#从头匹配,删除匹配到最短的s*a(*是通配符,表示任意长度的字符)
[root@test ~]# echo ${name2#s*a}
kura sakura sakura
#从头匹配,删除匹配到最长的s*a
[root@test ~]# echo ${name2##s*a}
[root@test ~]#
#井号'#'表示从头匹配,'%'表示从尾匹配,用法与上面一样,此处就不做赘述
#将匹配到的第一个sak替换为abc
[root@test ~]# echo $name2
sakura sakura sakura
[root@test ~]# echo ${name2/sak/abc}
abcura sakura sakura
#将匹配到的所有sak替换为abc
[root@test ~]# echo ${name2//sak/abc}
abcura abcura abcura
[root@test ~]#
#注意!替换不会修改原有变量
shell统计变量长度的命令
shell统计字符长度的方式有很多,但是效率最高速度最快的是echo ${#name}
#统计字符长度的命令
#通过管道符接wc命令的-L选项,意思为找出文本中最长的一行输出他的长度
[root@test ~]# echo ${name} | wc -L
6
#利用数值计算命令expr的length选项统计字符长度
[root@test ~]# expr length ${name}
6
#awk的length选项统计$0的长度
[root@test ~]# echo ${name} | awk '{print length($0)}'
6
比较统计字符长度命令的速度
#time计算命令执行时长,seq -s以:为分隔符产生序列并赋值给x,打印x到黑洞中
#使用${#x}
[root@test ~]# time for n in {1..1000};do x=`seq -s ":" 100`;echo ${#x} &> /dev/null;done
real 0m1.042s //实际运行时间
user 0m0.420s //用户态执行时间
sys 0m0.438s //内核态执行时间
[root@test ~]#
#使用wc -L 命令
[root@test ~]# time for n in {1..1000};do x=`seq -s ":" 100`;echo ${x} | wc -L &> /dev/null;done
real 0m2.343s
user 0m0.984s
sys 0m0.968s
#使用expr命令
[root@test ~]# time for n in {1..1000};do x=`seq -s ":" 100`;expr length ${name} &> /dev/null;done
real 0m1.831s
user 0m0.799s
sys 0m0.718s
#使用awk
[root@test ~]# time for n in {1..1000};do x=`seq -s ":" 100`;echo ${name} | awk '{print length($0)}' &> /dev/null;done
real 0m2.980s
user 0m1.212s
sys 0m1.363s
可见,${#变量}统计字符长度最快
批量修改文件名
准备测试环境:
[root@test ~]# mkdir test
[root@test ~]# cd test/
[root@test test]# touch test{1..5}_linux.jpg
[root@test test]# touch test{1..5}_linux.png
[root@test test]# ls
test1_linux.jpg test2_linux.jpg test3_linux.jpg test4_linux.jpg test5_linux.jpg
test1_linux.png test2_linux.png test3_linux.png test4_linux.png test5_linux.png
[root@test test]#
#现在要求去掉test目录下所有文件中的“linux”后缀
[root@test test]# for file_name in `ls /root/test/*_linux*`;do mv ${file_name} ${file_name//_linux/} ;done
[root@test test]# ls
test1.jpg test1.png test2.jpg test2.png test3.jpg test3.png test4.jpg test4.png test5.jpg test5.png
[root@test test]#
shell扩展变量
#“:-”用法
[root@test ~]# name1=tom
[root@test ~]# echo $name1
tom
[root@test ~]# echo $name2
[root@test ~]# echo $name3
#解释:定义name1变量值为tom,name2和name3为空
[root@test ~]# name3=${name1:-jerry} && echo $name3
tom
#解释:当name1有值时,将name1的值赋给name3并且打印
[root@test ~]# name3=${name2:-jerry} && echo $name3
jerry
#解释:当name2为空时,将":-"后面的"jerry"赋给name3并且打印
[root@test ~]# echo $name1
tom
[root@test ~]# echo $name2
#注意!此处name2仍然为空,上面的操作只是将Jerry赋给了name3,不会赋给name2
#“:=”用法
[root@test ~]# name1=tom
[root@test ~]# echo $name1
tom
[root@test ~]# echo $name2
[root@test ~]# echo $name3
#解释:与上面一样,只给name1赋值,name2和name3为空
[root@test ~]# name3=${name1:=jerry} && echo $name3
tom
[root@test ~]# name3=${name2:=jerry} && echo $name3
jerry
#解释:":-"和":="的区别在于给name2赋值,":-"会跳过,":="会赋值
[root@test ~]# echo $name1
tom
[root@test ~]# echo $name2
jerry
#":?"用法
#用法比较简单,有值时返回值,没有值时返回问号后面的内容
[root@test ~]# name1=tom
[root@test ~]# echo $name1
tom
[root@test ~]# echo $name2
[root@test ~]# echo ${name1:?变量为空}
tom
[root@test ~]# echo ${name2:?变量为空}
-bash: name2: 变量为空
[root@test ~]#
#":+"用法
[root@test ~]# name1=tom
[root@test ~]# echo $name1
tom
[root@test ~]# echo $name2
[root@test ~]# echo ${name1:+jerry}
jerry
[root@test ~]# echo ${name2:+jerry}
#解释:变量有值时,重新将加号后的文本赋给变量,如果变量没有值,就啥也不做
父子shell
执行脚本的三种方式:
1:source script 或 . script #在当前shell执行脚本,脚本中的变量不会在当前shell中失效
2:/bin/bash script #指定bash解释器执行脚本,开启子shell运行脚本
3:./script #与2一样都会开启子shell,变量只在子shell生效,会在当前shell失效
父shell
判断当前是否在父shell
#方法一:使用pstree命令查看到如下内容就说明在父shell
[root@test ~]# pstree
systemd─┬─NetworkManager───2*[{NetworkManager}]
.........
├─sshd───sshd───sshd───bash───pstree
.........
[root@test ~]#
#方法二:ps进程管理命令查看
[root@test ~]# ps -ef --forest
root 1179 987 0 12:16 ? 00:00:00 \_ sshd: root@pts/0
root 1219 1179 0 12:16 pts/0 00:00:00 \_ -bash
root 1810 1219 0 17:16 pts/0 00:00:00 \_ ps -ef --forest
[root@test ~]#
子shell
#linux内置变量$BASH_SUBSHELL,如果是在当前shell执行的命令就会返回0,如果是开启子shell执行,就会返回非0
[root@test ~]# echo $BASH_SUBSHELL
0
[root@test ~]# (cd /opt/ ; (cd /root/) ;echo $BASH_SUBSHELL)
1
[root@test ~]# (cd /opt/ ; (cd /root/ ;echo $BASH_SUBSHELL))
2
内置命令和外置命令
内置命令和外置命令的区别:
内置命令是指在系统启动时,就会加载到内存中,执行效率高,但占用资源
外置命令则需要用户先从硬盘中读取程序文件,再读入内存
#外置命令就是指用户自己下载的文件系统,是处于bash shell之外的程序
#一般存放在/bin;/usr/bin;/sbin;/usr/sbin,type命令可以判断命令是内置还是外置
[root@test ~]# type ps
ps is hashed (/usr/bin/ps) #提示是外置命令
[root@test ~]# type cd
cd is a shell builtin #提示是内置命令
[root@test ~]#
#查看所有的内置命令
[root@test ~]# compgen -b
.
:
[
alias
bg
..........
unset
wait
[root@test ~]#
#执行外部命令时,一般都会开启子shell执行
[root@test ~]# ps -f --forest
UID PID PPID C STIME TTY TIME CMD
root 1545 1544 0 09:29 pts/0 00:00:00 -bash
root 1621 1545 0 10:19 pts/0 00:00:00 \_ ps -f --forest
[root@test ~]#