赞
踩
#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 ~]#
内置命令在所有的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统计字符长度的方式有很多,但是效率最高速度最快的是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]#
#“:-”用法 [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}
#解释:变量有值时,重新将加号后的文本赋给变量,如果变量没有值,就啥也不做
执行脚本的三种方式:
1:source script 或 . script #在当前shell执行脚本,脚本中的变量不会在当前shell中失效
2:/bin/bash script #指定bash解释器执行脚本,开启子shell运行脚本
3:./script #与2一样都会开启子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 ~]#
#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 ~]#
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。