shell脚本编程
1、环境变量:
生效方法:
source filename
. filename
2、常用的环境变量:
/etc/profile
/etc/profile.d/.sh
/etc/bashrc
~/.bashrc
~/.bash_profile
放在/etc/的环境变量对所有用户生效
放在用户家目录下的环境变量仅对当前用户生效
3、环境变量的加载过程
需要输入用户名和密码的情况下:
/etc/profile->{/etc/profile.d/*.sh->/etc/profile.d/lang.sh->/etc/sysconfig/i18n,
~./bash_profile->~./bashrc->/etc/bashrc/->命令提示符}
在/etc/bashrc中定义了PS1,如果在流程中的某个配置文件被删除,/etc/bashrc就会调用失败,引起命令行显示不正常。
通过su切换,不需要账号密码的登陆:
/etc/bashrc/->/etc/profile.d/*.sh->lang.sh->/etc/sysconfig/i18n
i18n文件定义了操作系统的语言,而lang.sh又调用了i8n。
4、/etc/profile的作用:
定义用户登录时的:
USER变量
LOGNAME变量
PATH变量
HOSTNAME变量
HISTSIZE变量
umask
调用/etc/profile.d/*.sh文件
5、UMASK
文件的默认权限是666 “文件默认不给执行权限,如果要执行,需要额外赋予”
目录的默认权限是777
所以当umask为0022时,
创建一个文件所具有的权限是:
rw-rw-rw- - —-w–w– = rw-r–r– 644
创建一个目录所具有的权限是755
6、locale
查看系统的语言
查看语言调用可以通过/etc/profile.d/lang.sh查看
7、在退出登录时,会调用用户家目录中的bash_logout.sh文件,默认该文件为空,如果想在用户退出时执行什么操作,可以在此处添加,如:history -c清空历史命令
8、登陆警告信息
本地终端警告信息:
/etc/issue
远程终端警告信息
/etc/issue.net
但是,在该文件下,任何转义字符都无效,若想使其生效,需要修改远程登陆程序的配置:
/etc/ssh/sshd_conifg,加入”Banner /etc/issue.net”,然后重启sshd服务,使其生效。
**修改/etc/motd对本地和远程均能生效**
8、vim编辑器
语法高亮:syntax on
自动缩进:set autoindent
set cindent
9、shell编程的环境变量
环境变量一般是指用export内置命令导出的变量
设置历史命令的条数,在bashrc中有定义,HOSTSIZE,由于这些历史命令保存在本地磁盘上,所以,可以将默认条数。
设置环境变量:
变量名一般要大写
如果要设置环境变量,那么就要在给变量赋值后或者在设定变量时用export命令或这declare -x 来设定
export 变量名=value
变量名=value;export 变量名
declare -x 变量名=value
使环境变量永久生效的方法使写入bashrc配置中。
如果想要登陆后初始化或这显示加载内容,可以将文件放在/etc/profile.d/下,此文件下的.sh 文件在每次正常登录后都会重新执行一遍。
查看环境变量的方法:
env set 显示默认的环境变量
取消环境变量:
unset 消除本地和环境变量
unset name
10、变量
把一个命令的结果作为变量赋值的方法:
变量名=·ls· 不推荐反引号
变量名=$(ls) 把命令用$()括起来,推荐
example:
按天打包网站的站点目录程序,生成不同的文件名:
CMD=$(date +%F)
tar cf etc_$CMD.tar /etc
此时,打包出来的结尾为日期的文件
在引用变量时一定要注意{}的范围。
shell脚本中的特殊位置参数变量:
$0:获取当前执行的Shell脚本的文件名,如果执行脚本包含了路径,那么就包含脚本路径
$n:获取当前执行的shell脚本的第n个参数值。n=1..9
$#:获取当前执行的Shell脚本后边接的参数的总个数。
$*:获取当前shell脚本所有传递的参数。如果使用“$*”,那么他将所有参数视为单个字符串相当于"$1 $2"作为一个字符串输出
分离出文件名和路径名:
[root@localhost ~]# sh /root/test.sh
/root
test.sh
[root@localhost ~]# cat test.sh
#########################################################################
# File Name: test.sh
# author: Ginkgo
# mail: 907632998@qq.com
# Created Time: Sun 11 Jun 2017 01:33:22 PM EDT
#########################################################################
#!/bin/bash
dirname $0
basename $0
11、shell进程中的特殊状态变量
?:获取执行上一个指令的执行状态返回值,0为成功,非零为失败
:获取当前执行的Shell脚本的进程号可以使用
?查看软件是否安装成功
$?的用法:
1)判断命令,脚本,函数或程序是否执行成功
2)若在脚本中调用exit数字,则会把这个数字赋值给$?
3)如果在函数中调用了return 数字,则会把这个数字赋值给$?
-a file exists.
-b file exists and is a block special file.
-c file exists and is a character special file.
-d file exists and is a directory.
-e file exists (just the same as -a).
-f file exists and is a regular file.
-g file exists and has its setgid(2) bit set.
-G file exists and has the same group ID as this process.
-k file exists and has its sticky bit set.
-L file exists and is a symbolic link.
-n string length is not zero.
-o Named option is set on.
-O file exists and is owned by the user ID of this process.
-p file exists and is a first in, first out (FIFO) special file or named pipe.
-r file exists and is readable by the current process.
-s file exists and has a size greater than zero.
-S file exists and is a socket.
-t file descriptor number fildes is open and associated with a
terminal device.
-u file exists and has its setuid(2) bit set.
-w file exists and is writable by the current process.
-x file exists and is executable by the current process.
-z string length is zero.
example:实现系统中多次执行某一个脚本后的进程只有一个。
有时执行定时任务的脚本频率较快,并不知道上一个脚本是否真的执行完成,但是业务要求同一时刻内只有一个同样的脚本运行,可以用$$。
cat pid.sh
#!/bin/bash
pidpath=/tmp/a.pid
if [ -a "$pidpath" ]
then
kill $(cat $pidpath) >/dev/null 2>&1
rm -f $pidpath
fi
echo $$ >$pidpath
sleep 300
12、bash shell内置变量命令
1)echo 在屏幕上输出信息
参数:
-n 不换行输出内容
-e 解析转义字符
\n 换行
\r 回车
\t tab
\v 纵向制表位
2)exec 在不创建新的子进程的前提下,转去执行制定的命令。
example:
[root@localhost ~]# sh exec.sh
1
2
3
4
5
ok
[root@localhost ~]# cat exec.sh
#########################################################################
# File Name: exec.sh
# author: Ginkgo
# mail: 907632998@qq.com
# Created Time: Sun 11 Jun 2017 02:42:09 PM EDT
#########################################################################
#!/bin/bash
exec </tmp/tmp.log
while read line
do
echo $line
done
echo ok
3)read read变量表名
13、shell变量子串parameter为变量。
${parameter} 返回$parameter的内容
${#parameter} 返回字符串的长度,按字符,适用于特殊变量
${parameter:offset} 从offset处向后提取字符串 offset位置。
${parameter:offset:length} 从offset处向后提取length长度的字符串
${parameter#(%)word} 从开头(结尾)开始删除最短匹配的word子串。支持正则表达
${parameter##(%%)word} 从开头(结尾)删除最长匹配的word子串
${parameter/pattern/string} 用string替换第一个匹配的pattern
${parameter//pattern/string} 用string替换所有的pattern
进行批处理:
假设有一批文件,文件有相同的属性,比如都是以.jpg为后缀,可以同一改名。
执行:
for f in `ls *fin*.jpg`;do mv $f `echo ${f//_finished/}`;done
[root@localhost test]# cat test
stu_102999_1_finished.jpg
stu_102999_2_finished.jpg
stu_102999_3_finished.jpg
stu_102999_4_finished.jpg
stu_102999_5_finished.jpg
[root@localhost test]# cat test.sh
#!/bin/bash
for f in `cat ./test`
do
echo $f
touch $f
mv $f `echo ${f//_finished/}`
done
[root@localhost test]# ls
stu_102999_1.jpg stu_102999_2.jpg stu_102999_3.jpg stu_102999_4.jpg stu_102999_5.jpg test test.sh
[root@localhost ~]# sh p.sh
Please input first number: 1
1
Please input operators: -
Please input the second number: 1
1-1 = 0
[root@localhost ~]# ls
p.sh
[root@localhost ~]# cat p.sh
#!/bin/bash
read -p "Please input first number: " firstnum
echo $firstnum
read -p "Please input operators: " operations
if [[ "$operations" != "+" ]] && [[ "$operations" != "-" ]] && [[ "$operations" != "*" ]] && [[ "$operations" != "/" ]]
then
echo "Please 'echo' '+'or'-'or'*'or'/' "
else
read -p "Please input the second number: " secondnum
fi
echo "${firstnum}${operations}${secondnum}" = "$((${firstnum}${operations}${secondnum}))"
let 运算命令的用法:
let运算命令的格式为:let 赋值表达式
let赋值表达式的功能等同于"(())"
let i=i+8
expr 用于计算:
expr 2 + 2
expr 2 \* 2
在使用expr时,运算符用于计算数字左右至少有一个空格
*号要进行转义
在shell中进行运算要使用反引号
判断一个输入是否为数字:
[root@localhost ~]# cat q.sh
#!/bin/bash
read -p "Please inter a id: " id
expr $id + 1 >/dev/null
if [ 0 = $? ]
then
echo "THe id is num"
else
echo "error"
fi
在输入的一句话中打印出字数大于6的。
[root@localhost ~]# cat r.sh
#!/bin/bash
read -p "Please input a word: " word
echo $word
for i in $word
do
if [ ${#i} -gt 6 ];then
echo $i
fi
done
[root@localhost ~]# sh r.sh
Please input a word: hello my bestfirends
hello my bestfirends
bestfirends
grep :
grep :
-c 只输出匹配行的计数
-h 查询多文件时不显示文件名
-I 查询多文件时只输出包含匹配字符的文件
-n 显示匹配行及行号
-v 显示不包含匹配文本的所有行
正则:
\ 转义
^ 匹配正则表达式开始的行
$ 匹配正则表达式结束的行
\< 从匹配正则表达式的行开始
\> 到匹配正则表达式结束的行结束
[ ] 单个字符(其中的字符串都作为单个字符存在)
[-] 范围:如a-z
. 所有的单个字符
* 有字符,长度可以为0
a{2}含有两个a的行
a{2,}含有两个a以上的行
a{2,4}含有2到4个a的行
实例:
grep 'w\(es\).*\1' aa
在aa文件中,如果west被匹配,那么es就被写入内存中,并标记为1,然后搜索任意个字符,这些字符后边紧跟着另一个es(\1),找到就显示该行。
grep "r\(oo\)t\1" test
rootoo
grep 'test' d*
显示所有以d开头的文件中包含的行
grep '[a-z]\{5\}' aa
显示所有包含每个字符串至少有5个连续小写字符串的行
grep "^[^200]" a
在a文件中查找不一2 0 0 开头的行
[^ ]除了
[root@localhost ~]# cat test
200
2342342200
332002304
[root@localhost ~]# grep "^[^200]" test
332002304
``的作用:将里边命令的执行的输出返回给前边的变量
``中的东西并不会输出出来
方便和别的内容相结合。
echo -e 在最后会自动换行,可以解析转义字符
echo -n 输出之后不换行
然后使用read 命令读取
=read -p "Please input d word :" word
cut
-d 以什么做分割
-f 拿出第几列
awk
-F 以什么分割
'{print $1 '==>' $3}'
awk的默认分隔符为空格,不需要表示
awk中的常量NR表示第几行
df |awk '{if(NR==3) {print $1}}'
取出第三行的第一列 $0代表所有
df |awk '{if(NR==2){print}}'
打印第二行的所有
awk的转整形:int()将非整形元素变为整形
df |awk '{if(NR==3){print $1 }'如果$1为40%,可以使用
df |awk '{if(NR==3){print int($1) }'输出为40
输出文件的行:
df |awk 'END {print NR}'
输出文件的列:
df |awk 'END {print NF}'
awk里边的匹配:~/ /
不匹配:!~/ /
cat file |awk '$0 !~/192.169.244.1/ {print $0}'
取出file中不配配/ /的整行
awk:
1、调用awk
有三种方式调用awk,
第一种是命令行方式,如:
awk [-F field-separator] 'commands' inputfiles
commands是真正的awk命令
第二种是将所有的awk命令插入一个文件,并使awk程序可以执行,然后使用awk命令执行,以便通过键入脚本名称来调用他们
第三种是将所有的awk命令插入一个单独的文件然后调用
awk -f awk-script-file inputfiles
-f 指明在文件awk-scriipt-file中的脚本,inputfiles是使用awk进行浏览的文件
2、awk脚本
任何awk语句都由模式和动作组成,在一个awk脚本中可能有许多语句,模式部分决定动作语句何时触发及触发事件,处理即对数据进行操作,如果省略模式部分,动作将时刻保持执行状态。
模式可以是任何条件语句或复合语句或正则表达式,模式包括两个特殊字段BEGIN和END,使用BEGIN语句设置计数和打印头,BEGIN语句使用在任何文本浏览动作之前,之后文本浏览动作依据输入文件开始执行。END语句用来在awk完成文本浏览动作后打印输出文本总数和结尾状态标志。如果不特别指明,awk总是匹配或打印行数。
awk -F : 'BEGIN{print "start\tend\t" } {print $1"\t"$3} END{print "endoffile" }' passwd |head -n 5
**end结果在所有文本按行执行完成之后的结尾,不因head语句出现问题。
start end
root 0
bin 1
daemon 2
adm 3
实际动作在大括号内指明。
3、域和记录
awk执行时,其浏览域标记为$1,$2....,这种方法叫做域标识,,$0表示所有域。
4、规则:
确保整个awk命令用单引号括起来
确保命令内引号成对出现
确保用花括号括起动作语句,用圆括号括起条件语句。
5、awk中正则表达式
awk中的正则表达式用斜线括起来,如/awk/
元字符: \ ^ $ . [] | () * + ?
其中:使用+匹配一个或多个字符
?匹配出现的频率例如使用/XY?Z/匹配XYZ 或 YZ
条件操作符:
< > >= =< ~匹配 !~不匹配
awk -F : '{if($3 !~/0/) print $3}' passwd |head
6、awk的内置变量
awk有许多内置变量用来设置环境信息。这些变量可以被改变。
ARGC 命令行参数个数
ARGV 命令行参数排列
ENVIRON 支持队列中系统环境变量的使用
FILENAME awk浏览的文件名
FNR 浏览文件的记录数
FS 设置输入域分隔符,等价于命令行- F选项
NF 浏览记录的域个数
NR 已读的记录数
OFS 输出域分隔符 缺省为空格
ORS 输出记录分隔符 缺省为新行\n
RS 控制记录分隔符 缺省为新行
ARGC支持命令行中传入awk脚本的参数个数。
ARGV是ARGC的参数排列数组,其中每一元素表示为ARGV[n],n为期望访问的命令行参数。
# cat test |head -n 5
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@localhost mnt]# awk '{print NR,NF}' test
1 1
2 1
3 1
4 1
5 1
awk获得外部变量:
1、获得普通外部变量:
test ="hello,world"
# echo | awk -v test="$test" 'BEGIN{print test}'
helloworld
# echo | awk -v test="$test" '{print test}'
helloworld
格式:
awk -v 变量名=变量值 [-v 变量2=值2] ‘BEGIN{action}’ 使用-v选项可以使action在任何情况下获得变量值,但是要求变量在action之前
2、获得环境变量
调用awk内部变量ENVIRON,就可以获得环境变量,他是一个字典数组,环境变量名就是它的键值。
sed:
sed [-nefr] ‘[action]’
选项与参数:
-n 使用安静模式,在一般sed用法中,所有来自STDIN的数据一般都会列到屏幕上,如果加上-n,则只有sed特殊处理的哪一行或动作才会被列出来
-e :
直接在命令列模式上进行sed的动作编辑
-f :
直接将sed的动作写在一个文件内,-f filename则可以运行filename的sed动作
-r: sed的动作支持的是延伸性正则表示法的语法。
-i :直接修改读取的内容,而不是输出到终端。
动作说明:
[n1[n2]] function
n1,n2一般代表【进行动作的次数/范围】
function:
a:新增,a后边可以接字符串,而这些字符串会在新的一行出现在目前行的下一行
c:取代,c后边的字符串可以取代n1,n2之间的行
d:删除,d后边不加任何东西。
i:插入,i的后边可以接字符串,而这些字符串会在新的一行出现,当前行的上一行。
p:打印,将某个选择的数据打印,P通常和sed -n 一起使用
s:取代,直接进行取代动作,可以支持正则表达。
# sed '2,5d' test |wc -l
6
[root@localhost mnt]# cat test |wc -l
10
# sed '2,$d' test |wc -l 删除2到最后一行。
1
# sed '2i dirinktea' test |head -n 5
root:x:0:0:root:/root:/bin/bash
dirinktea
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
以行为单位的新增删除:
将/etc/passwd的内容列出并将第2~5行删除。
# nl /etc/passwd | sed '2,5d'
1 root:x:0:0:root:/root:/bin/bash
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
删除从第二行到最后一行
# nl /etc/passwd |sed '2,$d'
1 root:x:0:0:root:/root:/bin/bash
在第二行后添加drink tea
[root@localhost mnt]# nl /etc/passwd|sed '4,$d'|sed '3i drink tea '
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
drink tea
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
如果要添加新行,必须要使用\来进行新行的添加。
nl /etc/passwd|sed '4,$d'|sed '2a drink tea or \
> drink bear '
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
drink tea or
drink bear
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
以行为单位的替换和显示:
# nl /etc/passwd|sed '4,$d'|sed '2,3c helloword'
1 root:x:0:0:root:/root:/bin/bash
helloword
搜索并显示:
# nl /etc/passwd|sed '4,$d'|sed '/root/p'
1 root:x:0:0:root:/root:/bin/bash
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
不但输出匹配行,还会输出所有行
使用-n 参数,可以只输出匹配行。
匹配行的删除:
nl /etc/passwd | sed '/root/d'
2 daemon:x:1:1:daemon:/usr/sbin:/bin/sh
3 bin:x:2:2:bin:/bin:/bin/sh
数据的搜寻并执行命令
找到匹配模式eastern的行后,
搜索/etc/passwd,找到root对应的行,执行后面花括号中的一组命令,每个命令之间用分号分隔,这里把bash替换为blueshell,再输出这行:
nl /etc/passwd | sed -n '/root/{s/bash/blueshell/;p}'
1 root:x:0:0:root:/root:/bin/blueshell
如果只替换/etc/passwd的第一个bash关键字为blueshell,就退出
nl /etc/passwd | sed -n '/bash/{s/bash/blueshell/;p;q}'
1 root:x:0:0:root:/root:/bin/blueshell
最后的q是退出。
# ifconfig |sed -n '/inet/p'|grep -v inet6|awk 'BEGIN{print "ipaddr\t\tmask"} {print $2 "\t" $4}'
ipaddr mask
172.25.254.1 255.255.255.0
127.0.0.1 255.0.0.0
shell脚本中的条件测试:
语法:test <测试表达式> test和测试表达式之间至少有一个空格
[<测试表达式>] 通过条件测试表达式的方法 [ ] 边界和内容之间至少一个空格
[[<条件测试表达式>]] [ ]边界和空格之间至少一个空格
(()) 进行条件测试表达式的方法,一般用于if语句。不需要空格
example:
test -f file && echo true ||echo false
[ -f /tmp/file ] && echo true || echo false
测试的参数可以使用
-a file exists.
-b file exists and is a block special file.
-c file exists and is a character special file.
-d file exists and is a directory.
-e file exists (just the same as -a).
-f file exists and is a regular file.
-g file exists and has its setgid(2) bit set.
-G file exists and has the same group ID as this process.
-k file exists and has its sticky bit set.
-L file exists and is a symbolic link.
-n string length is not zero.
-o Named option is set on.
-O file exists and is owned by the user ID of this process.
-p file exists and is a first in, first out (FIFO) special file or named pipe.
-r file exists and is readable by the current process.
-s file exists and has a size greater than zero.
-S file exists and is a socket.
-t file descriptor number fildes is open and associated with a
terminal device.
-u file exists and has its setuid(2) bit set.
-w file exists and is writable by the current process.
-x file exists and is executable by the current process.
-z string length is zero.
字符串测试表达式
-n "字符串" 字符串的长度不为0,为真
-z "字符串" 字符串的长度为0,为真
"串1" = "串2" 1=2 或1==2
"串1"!= "串2" 1!=2 或1!==2
"="两端要留有空格,不然会产生逻辑错误。
字符串的比较一定要将字符串加入双引号再比较。
整数二元比较操作符:
-eq "=" -gt ">" -lt "<" -le "<=" -ge "》=" -ne "!="
在[]和test中使用的操作符:
-a == && -o == || ! == !
if 条件语句
1、单分支结构:
if <表达式>
then
指令
fi
或
if <表达式> ;then
指令
fi
case 条件语句:
case “变量” in
值1)
指令。。。
;;
值2)
指令….
;;
esac
while循环按行读取:
使用exec读入文件,然后按行处理:
exec < file
sum = 0
while read line
do
cmd
done
chattr 命令:
chattr命令适用于超级用户,change attrbute用于设置ext2,ext3文件的隐蔽属性,chattr[-RV] [+-][文件属性]文件或目录
-R:将目录下的所有子目录连同文件一起处理
-V:显示指令的执行过程
+: 开启某个文件或目录的该项属性
-: 关闭某个文件或目录的该项属性
=: 制定某个文件或目录的该项属性
a: 当设置为该属性时,该文件只能添加数据,不能删除
i; 当设置文件属性为i时,该文件不能被改名,删除和添加数据
chmod 只能改变文件的读写执行权限,更底层的属性则用chattr来实现。
如果用root账户仍然不能修改文件,就要考虑文件是否被锁定,使用lsattr来查看。
chattr不能用于/ ,/dev /tmp /var目录。、
lsattr列出文件的隐藏属性
-a显示所有文件和目录
-d如果链接的是目录,则只列出目录的属性
-R递归,将子目录,文件都列出来。
find *** -exec cp {} /mnt/ \;
locate -u 更新locate库。
strings /lib64/libc.so.6 |grep GLIBC_