1. 变量相关知识
- 静态编译语言:使用变量前,先声明变量类型,之后类型不能改变,在编译时检查,如:java,c
- 动态编译语言:不用事先声明,可随时改变类型,如bash,Python
- 强类型语言:不同类型数据操作,必须经过强制转换才同一类型才能运算,如java , c# ,python
- 如:以下python代码
print(‘magedu’+ 10) 提示出错,不会自动转换类 型
print(‘magedu’+str(10)) 结果为magedu10,需要显示转 换类型
- 如:以下python代码
- 弱类型语言:语言的运行时会隐式做数据类型转换。无须指定类型,默认均为字符型;参与运算会自动进行隐式类型转换;变量无须事先定义可直接调用
如:bash 不支持浮点数,php,javascript
2. 变量基本概念
- 变量:命名的内存空间
- 变量:变量类型 作用:
- 数据存储方式
- 参与的运算
- 表示的数据范围
类型:
字符型
数值型:整型、浮点型(注意bash shell只能整型,不能处理浮点型)
3. Shell中变量命名法则:
- 不能使程序中的保留字:例如if, for
- 只能使用数字、字母及下划线,且不能以数字开头
- 见名知义
- 统一命名规则:驼峰命名法 :利用下划线或者每个名称开头都大写等等
3.1. Shell中命名建议规则:
- 变量名大写
- 局部变量小写
- 函数名小写
- 用英文名字,并体现出实际作用
4. bash中变量的种类—{特殊变量$}
- 根据变量的生效范围等标准划分下面变量类型
- 局部变量(bash默认赋值就是局部变量):生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效
- 环境变量:生效范围为当前shell进程及其子进程,子子进程..
- 本地变量:生效范围为当前shell进程中某代码片断,通常指函数
- 位置变量:
$1, $2, ...来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数 - 特殊变量:
$?, $0, $*, $@, $#,$$
$$:当前的shell进程编号PID :可用echo $$显示,或者echo $BASHPID; 上一级bashPID可以用 echo $PPID- $: Expands to the process ID of the shell. In a () subshell, it expands to the process ID of the current shell, not the subshell.
4.1. 局部变量—{set}
- 变量赋值:
name="value" - 可以使用引用value
(1) 可以是直接字串:name="root"
(2) 变量引用:name="\$USER"
(3) 命令引用:name=`COMMAND` ,name=$(COMMAND)
4.1.1. 注意点1—{pstree}:
- 之前也总结过,变量的引用用
$或者${},而命令的引用用 “或者$(),注意括号的区别. 一个是变量,一个是命令. - 变量引用:
${name} 或者 $name- " " 弱引用,其中的变量引用会被替换为变量值
- ‘ ‘ 强引用,其中的变量引用不会被替换为变量值,而保持原字符串
- 显示已定义的所有变量:
set注意set显示的不仅仅是变量,还有函数等 - 删除变量:
unset name
– pstree :进程树状结构 -p 可以显示PID
18:26[root@centos7 /data]# which init
/usr/sbin/init
18:26[root@centos7 /data]# ll /usr/sbin/init
lrwxrwxrwx. 1 root root 22 Mar 5 21:16 /usr/sbin/init -> ../lib/systemd/systemd
4.2. 环境变量—{export,declare -x,set,env,printenv}
- 环境变量声明、赋值:
声明加赋值:
export name=VALUE
declare -x name=VALUE
只声明:
export name
declare -x name
- 变量引用:
$name, ${name}
- 显示所有环境变量:
env
printenv
export
declare -x
- 删除变量:
unset name
- ()自动开启子进程
4.2.1. 注意点2(重要):
变量赋值的时候
变量=所附的值,它们之间不能有任何的空格,如果想要所附的值与=之前有空格,需使用” "" “ 等括起来才可(原因是会把空格当做分隔符,变量= 会看做命令从而找不到出现错误).最好方式就是都加上双引号,不要省略默认的双引号,避免空格(被看做分隔符)引起的错误。变量赋值如果后面的字符串不加上双引号,则以空格为分隔符)因此,如果是一个整参数但其中有空格,必须加上双引号包住
很多命令后面的参数也是这样,空格会把他们分成多个参数,比如 mkdir " /data/backup/`date "+%F %T"`" ,date后面的两个参数得加上双引号包住,同时又因为date后面两个参数之间有空格,如果mkdir后面整体不加上双引号,则会看作是两个文件夹并给它创建上了,一个在backup文件夹内,另一个在当前文件夹内.
可以用
echo $变量$变量同时显示两个变量 ,但不可echo $变量变量,这样会把变量变量看做一个变量, 同上面前两条相同的逻辑,都是以空格为分隔符.$名字,如果不以数字开头的名字的这个变量并不存在,则echo $名字则会输出空行$#.*,如果以数字开头,则会把$#这个看作是一个变量(参考$#当做引用参的特殊变量),echo $#.*输出它的值(若为空值就是输出空),然后输出后面的.*里的字符内容。注意原本的变量规定不能以数字开头。$###,如果要原封不动地输出(其中###是数字),则echo $###,要么用''单引号包含,要么用转义字符\,例如echo \$100如果要输出转义字符,则需要写成
\\,若要输出两个\,则要echo \\\\,NUM=1,可以输出echo No$NUM,不可输出echo $NUMNo,必须echo ${NUM}No或者echo "$NUM"No.注意:花括号是包含在变量外面的 ,双引号是包含在变量外加$字符整体外面的M=10 ,N=$M,则echo $N结果为10 ,但是如果这时候修改M的赋值, 比如M=20,则echo $N结果仍然为10 .主要是因为当创建N的时候虽然引用的是M的值,但是N变量会开创一个新的数据空间,存入一个10的数据,N和M指向的并不是同一个数据空间,只是内部的数值相同而已. 此时更改M的值,只是把M的数据空间内的值改掉,N指向的数据控件的值仍然是10,已经和M无关了.(疑问:到底是M的指向在赋值的时候重新更改了,N创建的时候和M指向的是同一个现在仍然没有变化;还是说N创建的时候指向的就是新开辟的数据空间,只是引用了M的数据把10这个数值存入进去而已?)
变量赋值的时候不仅可以引用别的变量的内容(第9个注意点,它只是省略了双引号),也可以引用命令的输出结果,不过注意要用反向双引号;
当用echo显示变量内容时如果想要保留命令输出结果中的各种换行符,以及变量赋值的时候输入的各种空格符等等(也就是一个变量开头就有空格符,同时它存储多行内容,比如如果有变量赋值的内容是一个命令并且有换行符的时候),此时需要在
$变量外加双引号,注意双引号是加在echo命令后面的$变量整体外面的。原因经过例子也可看出例如:18:02[root@centos7 /data]# Userinfo=" --1 `who` " 18:03[root@centos7 /data]# echo $Userinfo --1 root pts/0 2019-03-16 08:56 (gateway) root pts/1 2019-03-16 17:58 (gateway) 18:03[root@centos7 /data]# echo "$Userinfo" --1 root pts/0 2019-03-16 08:56 (gateway) root pts/1 2019-03-16 17:58 (gateway)
bash所有变量默认都是字符,例子:
18:03[root@centos7 /data]# Num1=10 18:06[root@centos7 /data]# Num2=20 18:06[root@centos7 /data]# Num3=Num1+Num2 18:06[root@centos7 /data]# echo $Num3 Num1+Num2 18:06[root@centos7 /data]# Num3=$Num1+$Num2 18:07[root@centos7 /data]# echo $Num3 10+20变量用完最好删除,注意删除的时候直接就
unset 变量名,不需要加$定义的普通局部变量只能在1当前终端.2当前shell.3不能关机 中使用,不能在1.其它终端(或者窗口),2.子shell或者3.父shell,或者4.重启后(会消失)使用。
bash是一个后台持续运行的程序,可以在bash中再开bash子进程,用exit退出
定义的环境变量也是只能在当前shell和子shell中使用,不能在父进程或者重启(也就是退出shell再重新进入一个shell)后使用
想要自己定义的环境变量开机存在,可以在
/etc/profile.d/evn.sh中写入export name=VALUE即可,不过这个值也只能开机时就是这个值,关机之前最后的值必须手动写入才可。可以自己写一个脚本让环境变量的值关机时保留存入这里。set命令不仅可以查看普通局部变量(比如PS1),还可以看环境变量,还可以看其他的函数,等等。注意PATH是环境变量,PS1不是。export和declare -x只能看环境变量
()自动开启子进程,里面命名的变量不会影响到父进程看例子:
19:49[root@centos7 /data/lintst]# (name=zhang;echo $name)
zhang
19:50[root@centos7 /data/lintst]# echo $name
19:50[root@centos7 /data/lintst]# (name=zhang;echo $name);echo $name
zhang
19:51[root@centos7 /data/lintst]# name=duan;(name=zhang;echo $name);echo $name
zhang
duan
但是如果
19:52[root@centos7 /data/lintst]# name=duan;(echo $name);echo $name
duan
duan
详细一点:
19:56[root@centos7 /data/lintst]# name=duan;echo 1$name;(name=zhang;echo 2$name);echo 3$name
1duan
2zhang
3duan
19:57[root@centos7 /data/lintst]# name=duan;echo 1$name;(echo 2$name);echo 3$name
1duan
2duan
3duan
更详细一点:
19:57[root@centos7 /data/lintst]# echo $name
duan
19:58[root@centos7 /data/lintst]# (echo $name)
duan
19:58[root@centos7 /data/lintst]# (name=zhang; echo $name)
zhang
19:59[root@centos7 /data/lintst]# echo $name
duan
19:59[root@centos7 /data/lintst]# (echo $name)
duan
----------------------------------------
经过测试发现,小括号的确开启了子进程,但注意$$和$BASHPID是不同的
- 具体更多区别参考 http://codingdict.com/questions/43918
- 以及man bash 中的特殊变量 special prameters
$ Expands to the process ID of the shell. In a () subshell, it expands to the process ID of the current shell, not the subshell.
-----------------------------------------
20:06[root@centos7 /data/lintst]# echo $name;echo a$$;(echo b$$;echo $name);echo $name;echo c$$;sleep 200
duan
a16131
b16131
duan
duan
c16131
但是这样的话:
echo $name;echo 1a$BASHPID;echo 2a$$;(name=wang;echo 1b$$;echo 2b$BASHPID;echo 3b$$;echo $name;);echo $name;echo 1c$$;echo 2c$BASHPID;sleep 200
duan
1a69041
2a69041
1b69041
2b70094
3b69041
wang
duan
1c69041
2c69041
4.3. bash内建的环境变量
PATH SHELL USER UID HOME PWD SHLVL :shell嵌套的深度,也就是开了几个shell LANG MAIL HOSTNAME HISTSIZE _ 下划线 :上一个命令的最后一个字符串(或者是参数,以空格为分隔符来判断;如果没有参数则就是命令本身)
- 附加:
echo ###### | passwd --stdin zhang
scp -r zhang@###.###.##.### /data/scripts /data/cpdir/
4.3.1. 只读变量:只能声明,但不能修改和删除—{readonly,declare -r,-x,-i}
- 声明只读变量:
readonly name
declare -r name - 查看只读变量:
readonly [-p] declare -r只读declare -x全局(环境)declare -i数字型- 注意:只读变量它删不掉也改不了,只能退出shell的时候才能去掉
- 如果想要强行删只读变量,则方法也有,就是利用gdb工具1. 先安装gdb yum install gdb 2. 利用下面的方式删除只读变量即可 cat << EOF| gdb > attach $$ > call unbind_variable("只读变量名") > detach > EOF
- 如果想要强行删只读变量,则方法也有,就是利用gdb工具
4.3.2. 位置变量:在脚本代码中调用通过命令行传递给脚本的参数—{shift}
$1, $2, ... 对应第1、第2等参数,shift [n]换位置
$0 命令本身
$* 传递给脚本的所有参数,全部参数合为一个字符串
$@ 传递给脚本的所有参数,每个参数为独立字符串
$# 传递给脚本的参数的个数
4.3.3. 注意点3:
- 注意:
$@ $*只在被双引号包起来的时候才会有差异,类似前面的 变量=`who` ;echo "$变量"需要用双引号引起来- 想要看出它两个的区别,需要在脚本中的次级脚本或者次级函数中(比如for循环)再次进行引用的时候才能看出来。比如初级脚本用
$*和$@,但次级脚本用"$*"和用"$@"就会有区别。 - 可以查看 https://www.cnblogs.com/Template/p/9182534.html 所写有详细解释。
- 想要看出它两个的区别,需要在脚本中的次级脚本或者次级函数中(比如for循环)再次进行引用的时候才能看出来。比如初级脚本用
set --清空所有位置变量- 注意,在history中
!n:m第n行历史的m个参数 ,*所有 ^第一个 $最后一个 $0命令本身如果用bash 脚本名的方式来执行,则不会显示脚本的路径,只显示脚本名称(我们要的就是这个效果)。但如果直接执行脚本的话会把脚本路径也给全部显示出来,所以要用basename $0.(复习:bash 脚本名不需要执行权限,直接执行脚本需要脚本有执行权限x)[06:15:33 root@localhost /data/dir/test]# cat test.sh #!/bin/bash echo $0; echo `basename $0`; [06:15:14 root@localhost /data/dir/test]# bash test.sh test.sh test.sh [06:15:20 root@localhost /data/dir/test]# ./test.sh ./test.sh test.sh [06:15:23 root@localhost /data/dir/test]# te teamd teamdctl teamnl tee telinit test testgdbm test.sh [06:15:23 root@localhost /data/dir/test]# test.sh /data/dir/test/test.sh test.sh$#传递参数的时候如果#数字超过10个则必须用${}给括起来不然会当做两个数字;比如$10和${10}的区别。$0如果有软链接指向脚本,则运行这个软连接的时候,$0是这个软链接的名字而不是它指向的脚本的名字,但是执行的却是脚本的代码,因此在脚本里面进行判断过后可以输出不同的功能;- 比如/usr/sbin/pidof 命令就是 指向的/usr/sbin/killall5,它俩并不是同一个功能的命令;
- 通过在脚本里面判断$0的值,从而执行不同的功能。
- 在脚本里面用
shift命令,可以让$1参数去掉,让$2变成$1,后面依次前进1位。当然也可以shift n, 可以一次移位多个。- 利用
shift让脚本每次只处理最近的第一个参数,直到处理完所有的参数,从而实现处理不确定数目的参数的功能(当然也用for循环也行,看自己的喜好)
- 利用
5. 进程使用退出状态来报告成功或失败
0 代表成功,1-255代表失败
$? 变量保存最近的命令退出状态
- 例如:
ping -c1 -W1 hostdown &> /dev/null
echo $?
5.1. bash自定义退出状态码
exit [n]:可以自定义退出状态码
- 注意:脚本中一旦遇到exit命令,脚本会立即终止;后面的命令不再执行;终止退出状态取决于exit命令后面的数字 (没有的话默认还是0)
- 注意:如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码
- 利用这个自定义的返回值(可以同时配合函数中的return),可以来判断脚本执行到了什么位置退出或者什么结果退出。
- 例子:可以用它来判断命令执行错误与否
ping -c1 -w1 ###.###.###.### &>/dev/null
echo $?
留言