一:基础介绍
1优先级
别名-->内部命令-->外部命令
2. 脚本调试
sh -x foo.sh
sh -e foo.sh
3三种定界符
"字符串。。。。$变量名" #双引号用于界定不连续的字符,支持变量
‘字符串。。。。$ # ?‘ #单引号无法引用变量
`command` 或者 $(command) 【命令替换】 #反撇号用于执行命令,等同于$()
二:运算
1 Shell整数运算
方法1:
expr 表达式
方法2:
echo $[表达式]
echo $((表达式))
自增运算
let A++ 或者 ((A++))
let 表达式 或者 ((表达式))
2小数用管道交给 bc
echo "表达式" | bc
三:变量
1 变量基本操作 ——
变量名=值
echo $变量名
2 变量的几种类型 ——
自定义变量
环境变量:变量名一般全大写字母、用户登录后自动设好
位置变量:$1、$2、$3。。。。${10}。。。
预定义变量:
$0表示Shell本身的文件名,$#表示变量的个数,$*将所有位置变量作为一个整体,[email protected]
将所有位置变量作为一个字符串,$! 表示最后运行的后台Process的PID,$? 表示返回值
3 read -p "请输入你的名字:" name#输入的数值将赋值给变量name
四: 条件判断与循环
1 文件和目录的状态测试
-e filename如果 filename存在,则为真[ -e /var/log/syslog ]
-d filename如果 filename为目录,则为真[ -d /tmp/mydir ]
-f filename如果 filename为常规文件,则为真[ -f /usr/bin/grep ]
-r filename如果 filename可读,则为真[ -r /var/log/syslog ]
-w filename如果 filename可写,则为真[ -w /var/mytmp.txt ]
-x filename如果 filename可执行,则为真[ -x /usr/bin/grep ]
-s filename如果文件有数据,则为真[ -s /etc、passwd ]
#方括号和数据之间必须有空格,前面加!表示取反[ ! -x /usr/bin/grep ]
2 整数值比较:
num1-eq num2等于[ 3 -eq $mynum ]
num1-ne num2不等于[ 3 -ne $mynum ]
num1-lt num2小于[ 3 -lt $mynum ]
num1-le num2小于或等于[ 3 -le $mynum ]
num1-gt num2大于[ 3 -gt $mynum ]
num1-ge num2大于或等于[ 3 -ge $mynum ]
3 字符串比较:
-z string如果 string长度为零,则为真[ -z "$myvar" ]
-n string如果 string长度非零,则为真[ -n "$myvar" ]
string1== string2如果 string1与 string2相同,则为真[ "$myvar" == "one two three" ]
string1!= string2如果 string1与 string2不同,则为真[ "$myvar" != "one two three" ]
&& || ;
4 函数及中断控制
break 放弃当前的整个循环语句,---》跳到done之后
continue 放弃当前这一次循环语句,---》返回while [ 测试条件 ]
exit 退出当前的整个脚本
shift 处理位置变量
5 if选择结构 ——
单分支:
if [ 条件测试 ];
then
命令序列....
fi
双分支:
if [ 条件测试 ]
then
命令序列1....
else
命令序列2....
fi
多分支:
if [ 条件测试1 ]
then
命令序列1....
elif [ 条件测试2 ]
then
命令序列2....
else
命令序列3....
fi
6 for循环结构 ——
for 变量名 in 值1 值2 值3 ....
do
命令序列....
done
4 while循环结构 ——
while [ 条件测试 ]
do
命令序列....
Done
7 当条件成立的时候结束循环
until [ .. .. ]
do
cmd1 .. ..
done
8 类C语言风格的for循环
for ((i=1;i<10;i++))
do
cmd1 .. ..
Done
9 case分支
case $1 in
模式1)
cmd1 .. ..
;;
模式2)
cmd2 .. ..
;;
.. ..
*)
cmd n .. ..
esac
10 例子(添加用户)
[[email protected] day03]# cat uadm.sh
#!/bin/bash
if [ "$1" == "--add" ] && [ -f "$2" ]
then
for UNAME in $(cat $2)
do
useradd $UNAME
echo 123456 | passwd --stdin $UNAME
chage -d 0 $UNAME
done
elif [ "$1" == "--del" ] && [ -f "$2" ]
then
for UNAME in $(cat $2)
do
userdel -r $UNAME
done
else
echo "Usage: $0 --add|--del users.txt"
exit 1
Fi
五: 子字符串截取 及变量附初值
1 [[email protected] day03]# var1="CentOS6.5"
[[email protected] day03]# echo ${var1:6:3}
6.5
[[email protected] day03]# expr substr "$var1" 7 3
6.5
[[email protected] day03]# echo "$var1" | cut -c 7-9
6.5
2 ${var1:起始位置:长度}
${var1::长度} 【从第0个字符开始截取...】
${var1:起始位置} 【..截取到末尾】
${var1:0-起始位置:长度} 【0-倒数位置】
${var1:0-起始位置} 【0-倒数位置】
3 echo $var1 | cut -c 起始位置-结束位置
echo $var1 | cut -c -结束位置
echo $var1 | cut -c 起始位置-
echo $var1 | cut -c 字符位置
echo $var1 | cut -c 位置1,位置2.. ..
echo $var1 | cut -d "分隔字符" -f 起始字段-结束字段
.. ..
echo $var1 | cut -d "分隔字符" -f 字段1,字段2.. ..
4 echo ${#var1} 【获取变量值长度】
5 子字符串替换 ——
${var/old/new}【只替换第一个】
${var//old/new} 【替换全部】
6 字符串的掐头去尾——
${变量名#*关键词}【最小匹配】
${变量名##*关键词} 【最大匹配】
${变量名%*关键词}【最小匹配】
${变量名%%*关键词} 【最大匹配】
# 在 % 的左边
7 basename "文档路径" 【取文档名称】
dirname "文档路径" 【取文档的存放目录】
8 变量初值
${var1:-值}
${var1:=值}
六:函数
如何定义一个函数 ——
1 函数名() {
cmd .. ..
}
-- 或者 --
2 function 函数名 {
cmd .. ..
}
如何使用一个函数 ——
函数名
函数名 参数1 参数2
#!/bin/bash
name () {
sum=`expr $0 + $2`
echo $sum
}
name $1 $2
3 例子(生成mac地址)
#!/bin/bash
macgen() {
MAC="00:0c"
for ((i=1;i<=4;i++))
do
MAC=$MAC":$(uuidgen | cut -b -2)"
done
echo $MAC
}
macgen
七:启动脚本编写
1 编写系统服务脚本 ——
# vim /etc/init.d/服务名
#!/bin/bash
# chkconfig: 运行级别 启动顺序 停止顺序
# description: 关于本服务的描述
.. ..
2 chkconfig --add 服务脚本名
3 序列问题
rev:以字符为单位倒序输出文本
tac:以行为单位倒序输出文本
八:expect (非交互)
1 expect常用指令 ——
#!/bin/bash
spawn 交互进程
expect "期望的文本1" { send "提交的文本1\r" }
expect "期望的文本2" { send "提交的文本2\r" }
.. ..
expect eof【End of File】
#interact
2 如何将expect指令合成到shell脚本当中vb——
#!/bin/bash
.. .. Shell语句
expect - <<EOF
.. .. expect语句
.. ..
EOF
.. .. Shell语句
3 远程登陆脚本
#!/bin/bash
host=192.168.4.5
user=mike
password="123456"
#expect -f exp.txt
expect - <<EOF
spawn ssh [email protected]$host
expect {
"(yes/no)?" { send "yes\r" ; exp_continue}
"password:" { send "$password\r" }
}
expect "\[$user\@" { send "pwd > /tmp/$user.txt ; exit\r" }
expect eof
EOF
九 正则表达式
1 非打印字符
推荐使用egrep而不采用grep -e
cx匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 ‘c‘ 字符。
\f匹配一个换页符。等价于 \x0c 和 \cL。
\n匹配一个换行符。等价于 \x0a 和 \cJ。
\r匹配一个回车符。等价于 \x0d 和 \cM。
\s匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\t匹配一个制表符。等价于 \x09 和 \cI。
\v匹配一个垂直制表符。等价于 \x0b 和 \cK。
echo -e "aaaaaa\nbbbbbb"
echo -e "aaaaaa\tbbbbbb"
echo -e "aaaaaa\vbbbbbb"
echo -e "aadddddddddda\vbbb\t\tccc\nddd"
2 特殊字符
所谓特殊字符,就是一些有特殊含义的字符,若要匹配这些特殊字符,必须首先使字符"转义",即,将反斜杠字符 (\) 放在它们前面。
$匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 ‘\n‘ 或 ‘\r‘。要匹配 $ 字符本身,请使用 \$。
( )标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。
*匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。
+匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。
.匹配除换行符 \n之外的任何单字符。要匹配 .,请使用 \。
[标记一个中括号表达式的开始。要匹配 [,请使用 \[。
?匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。
\将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, ‘n‘ 匹配字符 ‘n‘。‘\n‘ 匹配换行符。序列 ‘\\‘ 匹配 "\",而 ‘\(‘ 则匹配 "("。
^匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。
{标记限定符表达式的开始。要匹配 {,请使用 \{。
|指明两项之间的一个选择。要匹配 |,请使用 \|。
{n}n 是一个非负整数。匹配确定的 n 次。例如,‘o{2}‘ 不能匹配 "Bob" 中的 ‘o‘,但是能匹配 "food" 中的两个 o。
{n,}n 是一个非负整数。至少匹配n 次。例如,‘o{2,}‘ 不能匹配 "Bob" 中的 ‘o‘,但能匹配 "foooood" 中的所有 o。‘o{1,}‘ 等价于 ‘o+‘。‘o{0,}‘ 则等价于 ‘o*‘。
{n,m}m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。‘o{0,1}‘ 等价于 ‘o?‘。请注意在逗号和两个数之间不能有空格。
\b匹配一个字边界,即字与空格间的位置。
\B非字边界匹配。
确定重复出现
^[a-zA-Z_]$所有的字母和下划线
^[[:alpha:]]{3}$所有的3个字母的单词
\t{2}两个制表符
(pattern)匹配 pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 ‘\(‘ 或 ‘\)‘。
(?:pattern)匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, ‘industr(?:y|ies) 就是一个比 ‘industry|industries‘ 更简略的表达式。
(?=pattern)正向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,‘Windows (?=95|98|NT|2000)‘ 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern)负向预查,在任何不匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如‘Windows (?!95|98|NT|2000)‘ 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
x|y匹配 x 或 y。例如,‘z|food‘ 能匹配 "z" 或 "food"。‘(z|f)ood‘ 则匹配 "zood" 或 "food"。
[xyz]字符集合。匹配所包含的任意一个字符。例如, ‘[abc]‘ 可以匹配 "plain" 中的 ‘a‘。
[^xyz]负值字符集合。匹配未包含的任意字符。例如, ‘[^abc]‘ 可以匹配 "plain" 中的‘p‘、‘l‘、‘i‘、‘n‘。
[a-z]字符范围。匹配指定范围内的任意字符。例如,‘[a-z]‘ 可以匹配 ‘a‘ 到 ‘z‘ 范围内的任意小写字母字符。
[^a-z]负值字符范围。匹配任何不在指定范围内的任意字符。例如,‘[^a-z]‘ 可以匹配任何不在 ‘a‘ 到 ‘z‘ 范围内的任意字符。
\D匹配一个非数字字符。等价于 [^0-9]。
\w匹配包括下划线的任何单词字符。等价于‘[A-Za-z0-9_]‘。
\W匹配任何非单词字符。等价于 ‘[^A-Za-z0-9_]‘。
\xn匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,‘\x41‘ 匹配 "A"。‘\x041‘ 则等价于 ‘\x04‘ & "1"。正则表达式中可以使用 ASCII 编码。
\num匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,‘(.)\1‘ 匹配两个连续的相同字符。
\n标识一个八进制转义值或一个向后引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为向后引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。
\nm标识一个八进制转义值或一个向后引用。如果 \nm 之前至少有 nm 个获得子表达式,则 nm 为向后引用。如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的向后引用。如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm。
\nml如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。
\un匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (?)。
cat /etc/passwd | egrep "\bin"#in位于单词的首
cat /etc/passwd | egrep "in\b"#in位于单词的尾
cat /etc/passwd | egrep "\Bin"#in不位于单词的首
cat /etc/passwd | egrep "in\B"#in不位于单词的尾
cat /etc/passwd | egrep "o{1,3}"和cat /etc/passwd | egrep "o{2,3}"和cat /etc/passwd | egrep "o{2,}"
cat /etc/passwd | egrep ^root
cat /etc/passwd | grep -e bash$
cat /etc/passwd | egrep oo* | wc -l和cat /etc/passwd | egrep \(oo\)* | wc -l#后者多是因为将oo看作一个整体
cat /etc/passwd | egrep r.t和cat /etc/passwd | egrep r..t
cat /etc/passwd | egrep "t\w*d"和cat /etc/passwd | egrep "t\w+d"和cat /etc/passwd | egrep "t\w?d"#*比+多一个0匹
3 邮件过滤
[email protected]
[email protected]
[a-z0-9]+ \. [a-z0-9]{2,4}
([a-z0-9]+\.){1,3} [a-z0-9]{2,4}
十:sed
1 sed 处理文本的两种方式 ——
sed [选项] ‘[定址符]处理动作‘ 文本文件...
其他命令 | sed [选项] ‘[定址符]处理动作‘
常用的选项 ——
-n :使用安静(silent)模式。在一般 sed 的用法中,所有来自 STDIN 的数据一般都会被列出到终端上。但如果加上 -n 参数后,则只有经过sed 特殊处理的那一行(或者动作)才会被列出来。
-e :直接在命令列模式上进行 sed 的动作编辑;
-f :直接将 sed 的动作写在一个文件内, -f filename 则可以运行 filename 内的 sed 动作;
-r :sed 的动作支持的是延伸型正规表示法的语法。(默认是基础正规表示法语法)
-i :直接修改读取的文件内容,而不是输出到终端。
2 定址符的写法 ——
sed -i ‘/gpgcheck/s/\(gpgcheck\)/#(\1)/g‘ CentOS-*
行号
行号1,行号2
正则表达式
行号1,正则表达式
正则表达式,行号2
5,~7从第5行开始,到下一个7的倍数行结束
5~7取每7行当中的第5行
5~5取每5行当中的第5行
7~5从第7行开始,以后取每5行的第5行
常用的处理动作 ——
n1, n2 :不见得会存在,一般代表『选择进行动作的行数』,举例来说,如果我的动作是需要在 10 到 20 行之间进行的,则『 10,20[动作行为] 』
a :新增,a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)
c :取代,c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行
d :删除,因为是删除,所以 d 后面通常不接任何字符
i :插入,i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行)
p :列印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行
s :取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法r、w
H、h、G、g
egrep ‘[a-z0-9]{3,}@([a-z0-9]+\.){1,3}[a-z0-9]{2,4}‘ t4.txt
一一替换
Sed ‘y/abcdefghijklmnopqrstuvwlmn/ghijklmnopqrstuvwlmnabcdef/’ /etc/passwd 即匹配一个替换一个
十一:awk
1 awk的内置变量
两种执行方式 ——
awk -F: ‘[条件]{编辑指令}‘ 文件.. ..
其他命令 | awk -F: ‘[条件]{编辑指令}‘
ARGC 命令行参数个数
ARGV 命令行参数排列
ENVIRON 支持队列中系统环境变量的使用
FILENAME awk浏览的文件名
FNR 浏览文件的记录数
FS 设置输入域分隔符,等价于命令行 -F选项
NF 浏览记录的域的个数
NR 已读的记录数
OFS 输出域分隔符
ORS 输出记录分隔符
RS 控制记录分隔符
awk ‘BEGIN{FS=":"}/^root/{print $1,$NF}‘ /etc/passwd#FS分隔符
awk ‘BEGIN{FS=":"}{print NR,$1,$NF}‘ /etc/passwd#NR行号,NF总列数
awk ‘BEGIN{FS=":";OFS="^^"}/^root/{print FNR,$1,$NF}‘ /etc/passwd#OFS输出分割数
awk -F ‘:‘ ‘{print "文件名是:"FILENAME",这是第:"NR行,此行一共有:"NF"列,此行全部内容为:"$0}‘ /etc/passwd
#默认分隔符为单个或多个tab和空格,$0表示整行,$1和$3表示第1列和第三列,用逗号分隔
2 print和printf
awk中同时提供了print和printf两种打印输出的函数。
其中print函数的参数可以是变量、数值或者字符串。字符串必须用双引号引用,参数用逗号分隔。如果没有逗号,参数就串联在一起而无法区分。这里,逗号的作用与输出文件的分隔符的作用是一样的,只是后者是空格而已。
printf函数,其用法和c语言中printf基本相似,可以格式化字符串,输出复杂时,printf更加好用,代码更易懂。
awk -F ‘:‘ ‘{printf("filename:%10s,linenumber:%s,columns:%s,linecontent:%s\n",FILENAME,NR,NF,$0)}‘ /etc/passwd
3 实验-可多重条件查找,输出多次
awk ‘BEGIN {FS=":"} ; /0/ {print $0} ; /root/ {print $0}‘ /etc/passwd
#查找到有0的行输出,查找到有root的行输出,//之间采用正则表达式,$0表示输出整行
变量和赋值
awk ‘{count++;print $0;} END{print "user count is ", count}‘ /etc/passwd
#count为变量,默认初始值为0。之前的action{}里都是只有一个print,其实print只是一个语句,而action{}可以有多个语句,以;号隔开
awk ‘BEGIN {count=0;print "[start]user count is ", count} {count=count+1;print $0;} END{print "[end]user count is ", count}‘ /etc/passwd
#count虽然默认是0,但是妥当的做法还是初始化为0
4 实验-支持正则表达式
awk ‘/11+/ {print $0}‘ qintest
awk ‘/oo+/ {print $0}‘ /etc/passwd | awk ‘/^r/ {print $0}‘
awk ‘/oo+|^root/ {print $0}‘ /etc/passwd
awk ‘/1{3,}/ {print $0}‘ qintest
awk ‘/1{4}/ {print $0}‘ qintest
awk ‘/1{2,3}/ {print $0}‘ qintest
实验-BEGIN和END
cat /etc/passwd |awk -F ‘:‘ ‘BEGIN {print "name\tshell"} {print $1"\t"$7} END {print "nameend\tshellend"}‘
5 awk的流程控制 ——
经典去重 ——
awk ‘!x[$0]++‘ arr.txt
相当于 awk ‘!数组名[当前行的文本]{print $0}‘ arr.txt
第一次执行 x["/bin/bash"]++ 会失败
!x["/bin/bash"]++ 表示条件成立
第二、三……次执行 x["/bin/bash"]++ 会成功
!x["/bin/bash"]++ 表示条件不成立
6 awk文本处理练习:
1使用awk输出系统用户个数。
awk -F ":" ‘{j++}END{print j}‘ /etc/passwd
2使用awk输出系统内建用户个数。
awk -F ":" ‘$3<500{i++}END{print i}‘ /etc/passwd
3使用awk输出系统外建用户个数。
awk -F ":" ‘$3>500{j++}END{print j}‘ /etc/passwd
4使用awk 一次性 输出系统用户(UID<500)个数、其他用户个数、用户总数。
awk -F ":" ‘{i++}$3<500{j++}$3>=500{z++}END{print "user sum is " i;print "inside user is "j;print "outside user is "z}‘ /etc/passwd
5 输出文件中 5到10行的行号和 对应行的内容。
awk -F ":" ‘FNR<=10&&FNR>=5{print $0}‘ /etc/passwd
6 输出文件中偶数行的行号 和 对应行的内容。
awk -F ":" ‘FNR%2==0{print FNR,$0}‘ /etc/passwd
7 输出文件 uid 值是 500的用户的详细信息
awk -F ":" ‘$3==500{print $0}‘ /etc/passwd
8 输出文件中 sync用户的详细信息
awk -F ":" ‘$1=="sync"{print $0}‘ /etc/passwd
9 输出数字32到69之间,有数字7 或是 数字7 倍数的数。
seq 32 69|awk ‘$0~/7/ || $0%7==0{print $0}
10 统计不能登录系统的用户的个数
awk -F ":" ‘$7=="/sbin/nologin"{print $0}‘ /etc/passwd
11 查看uid号是5位数的用户的名字和uid号的值
awk -F ":" --posix ‘$3~/^[0-9]{3}$/{print $3}‘ /etc/passwd
awk -F ":" ‘$3~/...../{print $1,$3}‘ /etc/passwd
12 把名字里有数字的用户名输出。
awk -F ":" ‘$1~/[0-9]/{print $1}‘ /etc/passwd
13 输出文件前10行中的偶数行
awk -F ":" ‘FNR<=10&&FNR%2==0{print NR,$0}‘ /etc/passwd