一、shell的执行
1、执行命令但不输出结果到命令行
[root@localhost ~]$ command >/dev/null
2、多命令顺序执行
命令 1;命令 2 #顺序执行命令,命令间无逻辑关联
命令 1&&命令 2 #命令1正确执行才执行命令2,否则不执行
命令 1||命令 2 #命令1不正确执行才执行命令2,否则不执行
3、执行shell的命令
[root@localhost ~]$ chmod u+x test.sh
[root@localhost ~]$ ./test.sh
[root@localhost ~]$ bash test.sh
二、shell变量
1、shell变量名称定义规则:
- 命名只能使用英文字母、数字、下划线,不能以数字开头
- 等号前后不能包含空格
- 环境变量一般使用大写
- 单引号内都是普通字符
- 双引号内可以实现转义、且有些字符有特殊含义,如"$"有调用变量的值的含义
[root@localhost ~]$ a=1
[root@localhost ~]$ a="$a"2
[root@localhost ~]$ echo $a
12
#叠加变量a
[root@localhost ~]$ a=${a}3
[root@localhost ~]$ echo $a
123
#再叠加变量a
- 双引号内可以实现转义、且有些字符有特殊含义,如"$",可以用来调用命令
单引号、双引号、反引号用法
-反引号用法:反引号括起来的内容是系统命令,在Bash中会先执行它
[root@localhost ~]$ name=wulei
#定义变量name 的值是wulei
[root@localhost ~]$ echo '$name'
$name
#如果输出时使用单引号,则$name原封不动的输出
[root@localhost ~]$ echo "$name"
wulei
#如果输出时使用双引号,则会输出变量name的值 wulei
[root@localhost ~]$ echo `date`
2018年10月21日星期一18:16:33 CST
#反引号括起来的命令会正常执行
[root@localhost ~]$ echo '`date`'
`date`
#但是如果反引号命令被单引号括起来,那么这个命令不会执行,―date会被当成普通字符输出
[root@localhost ~]$ echo "`date`"
2018年10月21日星期一18:14:21 CST
#如果是双引号括起来,那么这个命令又会正常执行
[root@localhost ~]$ echo ls
ls
#如果命令不用反引号包含,命令不会执行,而是直接输出
[root@localhost ~]$ echo `ls`
anaconda-ks.cfginstall.loginstall.log.syslog sh test testfile
#只有用反引号包括命令,这个命令才会执行
[root@localhost ~]$ echo $(date)
2018年10月21日星期一18:25:09 CST
#使用$(命令)的方式也是可以的
#在shell脚本中使用Linux命令,也需要使用$()或者``包裹命令,例如
num=$(cat /etc/passwd | grep $username | wc -l)
numt=`cat /etc/passwd | grep $username | wc -l`
位置参数变量
位置参数变量 | 作用 |
---|---|
$n | n为数字,$0表示当前 Shell 脚本程序的名称,$1-9 代表第一到第九个参数 , 十以上的参数需要用大括号包含,如 9代表第一到第九个参数,十以上的参数需要用大括号包含,如9代表第一到第九个参数,十以上的参数需要用大括号包含,如{10} |
$* | 这个变量代表命令行中所有的参数,$把所有的参数看成一个整体 |
$@ | 这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待 |
$# | 这个变量代表命令行中所有参数的个数 |
$*会把接收的所有参数当成一个整体对待,而$@则会区分对待接收到的所有参数。举个例子:
[root@localhost sh]$ vi parameter2.sh
#!/bin/bash
for i in"$*"
#定义for循环,in后面有几个值,for会循环多少次,注意“$*”要用双引号括起来
#每次循环会把in后面的值赋予变量i
#Shell把$*中的所有参数看成是一个整体,所以这个for循环只会循环一次
do
echo "The parameters is: $i"
#打印变量$i的值
done
## 带参数输入:sh parameter2.sh 1 2 3 5
## 输出结果:The parameters is: 1 2 3 5
x=1
#定义变量x的值为1,用于跟踪参数的索引
for y in"$@"
#同样in后面的有几个值,for循环几次,每次都把值赋予变量y
#可是Shel1中把“$@”中的每个参数都看成是独立的,所以“$@”中有几个参数,就会循环几次
do
echo "The parameter$x is: $y"
#输出变量y的值
x=$((x +1))
#然变量x每次循环都加1,为了输出时看的更清楚
done
## 带参数输入:sh parameter2.sh 1 2 3 5
## 输出结果:The parameters is: 1
# The parameters is: 2
# The parameters is: 3
# The parameters is: 5
x=1
for y in"$#"
#同样in后面的有几个值,for循环几次,每次都把值赋予变量y
do
echo "The parameter$x is: $y"
#输出变量y的个数
done
## 带参数输入:sh parameter2.sh 1 2 3 5
## 输出结果:The parameters1 is: 4
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/w918589859/article/details/108752592
举一反三,看看shell数组中${a[@]}、${a[*]}、${#a[@]}、${#a[*]}之间的关系
首先解释下shell遵循的几个关联原则:
- 使用变量时需要加上${}符号
- a=(e1 e2 e3 e4)是shell数组的表达,读取数组中元素的格式为${a[下标]}
- 一般情况下,$@、$*都表示传递给函数或脚本的所有参数,因此${a[@]}等价于${a[*]},${#a[@]}等价于${#a[*]}
- 但当他们被双引号包含后,$@会将每个参数看作单独的数据,彼此之间独立、$*将所有参数从整体上看做一份数据(看上面的bash示例即可)
- $# 这个变量代表命令行中所有参数的个数
因此:
- ${#a[@]}、${#a[*]}指的都是这个数组中所有参数的个数
- 直接打印${a[@]}、${a[*]}会一次性输出这个数组中所有参数
- 在for循环中,for i in “${a[@]}”,会循环多次输出,每次输出一个参数
- 在for循环中,for i in “${a[*]}”,会一次性输出这个数组中所有参数
再举个例子:
#!/bin/bash
pids=(num1 num2 num3)
echo "\${#pids[@]}表示参数总数: ${#pids[@]}"
echo "####################"
echo "\${#pids[*]}表示参数总数: ${#pids[*]}"
echo "######### 一般情况 ########"
echo "\${pids[@]}输出每个参数: ${pids[@]}"
echo "\${pids[*]}输出每个参数: ${pids[*]}"
echo "######### 带双引号区别 ########"
for i in "${pids[*]}";do
echo "双引号下\${pids[*]}作为一个整体输出:$i"
done
echo "####################"
for i in "${pids[@]}";do
echo "双引号下\${pids[@]}依然输出每个参数:$i"
done
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_39677803/article/details/125147594
输出为:
${#pids[@]}表示参数总数: 3
####################
${#pids[*]}表示参数总数: 3
######### 一般情况 ########
${pids[@]}输出每个参数: num1 num2 num3
${pids[*]}输出每个参数: num1 num2 num3
######### 带双引号区别 ########
双引号下${pids[*]}作为一个整体输出:num1 num2 num3
####################
双引号下${pids[@]}依然输出每个参数:num1
双引号下${pids[@]}依然输出每个参数:num2
双引号下${pids[@]}依然输出每个参数:num3
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_39677803/article/details/125147594
三、read交互式输入
用法:
read [-t 输出后等待时间 ] [-p 提示语 ] [-s (隐藏输入)] [-n 输入参数数量校验(若不符合则输入无效) ]
举例:
read -s -t 10 -p "your age is :" age
echo $age
echo -e "\n" #-e表示输出转义字符
四、运算符
算数运算符
用法:
`expr $a + $b `
`expr $a - $b `
`expr $a \* $b `
`expr $a / $b `
`expr $a % $b ` #取余
[ $a == $b ] #判断两数值是否相等,注意前后空格及花括号,返回布尔值
[ $a != $b ] #判断两数值是否不相等,注意前后空格及花括号,返回布尔值
关系运算符
对象:数字
-eq equal 相等
-ne not equal 不相等
-gt great than 大于
-lt less than 小于
-ge great than or equal 大于等于
-le less than or equal 小于等于
# 均返回布尔值
逻辑运算符
! 非运算
-o 或运算
-a 与运算
# 均返回布尔值
字符串运算符
= 相等
!= 不相等
-z 字符串长度为0,用法[ -z $a ]
-n 字符串长度不为0,用法[ -n $a ]
str 字符串不为空,用法[ $a ]
# 均返回布尔值
文件测试运算符
用于校验文件属性
-b file 是块设备文件,用法[ -b $file ]
-c file 是字符设备文件,用法[ -c $file ]
-d file 是目录,用法[ -d $file ]
-f file 是普通文件,用法[ -f $file ]
-r file 文件可读,用法[ -r $file ]
-w file 文件可写,用法[ -w $file ]
-x file 文件可执行,用法[ -x $file ]
-s file 文件大小是否为0,用法[ -s $file ]
-e file 文件是否存在,用法[ -e $file ]
# 均返回布尔值
-e举例:
[root@localhost ~]$ vi mk_dir.sh
#!/bin/bash
#创建目录,判断是否存在,存在就结束,反之创建
DIR="/tmp/dir"
if [ ! -e $DIR ]
then
mkdir -p $DIR
fi
echo "$DIR 创建成功"
五、流程控制
if条件语句
单分支语法:
if [ 条件语句 ]
then
...
fi
双分支语法:
if [ 条件语句 ]
then
...
else
...
fi
多分支语法:
if [ 条件语句 ]
then
...
elif [ 条件语句 ]
then
...
else
...
fi
case多分支语句
当只有一种条件关系且与多级时可以使用case语句
case $变量名 in
"值1")
...
;; ##每个条件分支必须以;;结尾
"值2")
...
;;
"值3")
...
;;
*) ## 如果以上都不符合,则执行这个分支
...
;;
esac #以esac结尾
for循环语句
for 变量 in 值1 值2 值3 值4 ...(可以是一个文件)
do
...
done
举例:
[root@localhost ~]$ vi sh/auto-tar. sh
#!/bin/bash
#批量解压缩脚本
cd/lamp
#进入压缩包目录
ls *.tar.gz > ls.log
#把所有.tar.gz结尾的文件的文件覆盖到ls.log 临时文件中
for i in $(cat ls.log) `
#或者这样写for i in `cat ls.log`
#读取ls.log文件的内容,文件中有多少个值,就会循环多少次,每次循环把文件名赋予变量i
do
tar -zxf $i &>/dev/null
#加压缩,并把所有输出都丢弃
done
rm -rf /lamp/ls.log
#删除临时文件ls.log
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/w918589859/article/details/108752592
for 变量 in ((初始值;循环控制条件;变量变化))
do
...
done
举例:
[root@localhost ~]$ vi sh/add. sh
#!/bin/bash
#从1加到100
s=0
for (( i=1;i<=100;i=i+1 ))
#定义循环100 次
do
s=$(( $s+$i )) #注意这里是算术运算,因此需要2组小括号,
#每次循环给变量s赋值
done
echo "The sum of 1+2+...+100 is : $s"
#输出1加到100的和
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/w918589859/article/details/108752592
while循环语句
while [ 条件判断语句 ]
do
...
done
exit、break、conti
- exit:直接退出整个程序,可以自定义返回值,如exit 10
- break:退出所在循环语句,继续直接程序后续语句
- continue:退出本次循环,不执行循环内后面的语句,进入下一次循环
六、函数
function 函数名(){
...
}
举例:
#!/bin/bash
function add(){
s=$(($a+10))
echo $s
}
read -p "请输入原始数据:" a
add $a
七、字符截取、替换和处理命令
正则表达式
字符 | 描述 | 示例 |
---|---|---|
. | 匹配一个任意字符 | a.b可以匹配a1b、aab等 |
^ | 匹配以指定字符串开头字符串 | ^ab可以匹配abc,不能匹配aab |
$ | 匹配以指定字符串结尾字符串 | ab$可以匹配aab,不能匹配abc |
[] | 匹配括号中的任意一个字符 | [abc]d可以匹配ad、bd、cd |
[^] | 匹配^字符之外的任意一个字符 | [^abc]d不能匹配ad,能匹配kd |
[-] | 匹配指定范围内的任意一个字符,需要顺序递增 | [0-9][a-f][A-F]可以匹配一位十六进制数据 |
? | 匹配?之前的单元1次或0次 | a?k可以匹配ak、k,不能匹配aak |
* | 匹配*之前的单元0次或多次 | g*可以匹配ggoo |
+ | 匹配+之前的单元1次或多次 | |
{N} | 匹配{}之前的单元N次 | [1-9][0-9]{2}匹配从100-999的整数 |
{N,} | 匹配{}之前的单元至少N次 | [1-9][0-9]{2,}匹配大于100的整数 |
{,M} | 匹配{}之前的单元至多M次 | [1-9]{,1}等价于[1-9]? |
{N,M} | 匹配{}之前的单元至少N次,至多M次 | [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}可以匹配IP地址 |
| 连接2个子表达式,表示或的关系 | n(o | e)可以匹配no和ne |
grep 中使用正则表达式
- egrep等价于grep -e,表示采用extended正则表达式语法
- grep表示采用basic语法
- fgrep等价于grep -F,表示只搜索指定字符串不搜索正则表达式,不按正则表达式语法解释
- extended规范下正则表达式需要使用单引号’ '表示,否则可能会被shell解释器解释成其他含义,且?+{}()|不需要加转移字符\,如果使用basic规范,?+{}()|需要加转移字符\才能表达为正则表达式
sed中使用正则表达式
sed支持使用特殊元字符进行查找和替换,sed -n ‘/^mmm/p’ file1可以查找file1文件中以mmm开头字符串
cut字符截取
cut用法:
cut [选项] 文件名
命令 | cut [选项]
参数:
-d 分隔符,默认分隔符是制表符tab
-f 与-d一起使用时,-d将字符串分割后,用-f提取出第几段;默认分隔符下,表示列号,提取第几列
-c 以字符char为单位截取固定字符区间
-b 以字节byte为单位截取固定字节区间
-n 与-b一起使用,表示不分割占多字节的字符,比如一个汉字占2给字符,加上-n就不会将其分割成多个字节进行截取
--complement 用于排除所指定的字段
--output-delimiter 更改输出内容的分隔符
举例:
cut -b 4 #截取每一行的第4个字节
cut -b 1,3,4 #截取每一行的第1,3,4个字节(此时输出内容没有分隔符)
cut -b 1,3,4 --output-delimiter=" " #此时每一行的第1、3、4个字节之间就有了 分隔符
cut -b 1-3 #截取每一行的第1-3个字节
一般情况下,-c和-b的功能一样,但对于占多个字节的中文字符,使用-b时,一般加-n
举例:
[root@localhost]:echo "今天也开心" |cut -c 1-2
今天
[root@localhost]:echo "今天也开心" |cut -b 1-3
今
[root@localhost]:echo "今天也开心" |cut -b 1-3 -n
今天也
-f的几种参数传值:
- N:选取第N段,从1开始计数
- N-:从第N段到最后一段
- N-M:从第N段到第M段
- N,M:第N段和第M段
- -M:从第一段到第M段
举例:
cut -d "%" -f 1 #以%作为分隔符分隔数据,并取出第一个数
–complement 用法举例:
[root@localhost]echo "今。天。也。开。心" | cut -d "。" --complement -f 2 #排除第二个字段
今也开心
printf使用
https://blog.csdn.net/cumtglad/article/details/139837186
printf相比于print的区别:
- printf 支持%d %s %i等格式化输出
- printf不能自动换行,需要手动添加\n
awk基本使用
基础语法:
awk 参数 参数值 '条件1 {动作1} 条件2 {动作2} ... ' 文件名
命令|awk 参数 参数值 '条件1 {动作1} 条件2 {动作2} ... '
条件:
一般使用关系表达式作为条件,如:
- x > 10
- x == y
- A ~ B 判断字符串A中是否包含能匹配B表达式的子字符串
- A !~ B 判断字符串A中是否不包含能匹配B表达式的子字符串
也可使用awk保 留字: - BEGIN 在awk程序一开始时,尚未读取任何数据之前执行。BEGIN后的动作只在程序开始时执行一次
- END 在awk程序处理完所有数据,即将结束时执行。END后的动作只在程序结束时执行一次
还可使用正则表达式//,//中间可以填写正则表达式或字符
动作:
- 格式化输出(printf、print)
- 流程控制语句
举几个例子介绍下条件和动作的使用:
awk 'BEGIN{printf "这是每个学生的平均成绩\n" } {printf $2 "\t" $6 "\n"}’ student.txt #stuedent表中列为姓名,列6为成绩
#输出:
这是每个学生的平均成绩
张三 66
李四 77
王五 88
awk 'END{printf "上面是姓李的学生的平均成绩\n"} $2 ~ /李/ {printf $2 "\t" $6 "\n"}’ student.txt #stuedent表中列为姓名,列6为成绩
#输出:
李四 77
上面是姓李的学生的平均成绩
注意:BEGIN和END可以一起使用
awk 'cat student.txt | awk '$6 > 70 {printf $2 "\t" $6 "\n"}’ #stuedent表中列为姓名,列6为成绩
#输出:
李四 77
王五 88
#输出成绩大于70分的学生姓名和成绩
awk 'cat student.txt | awk '/[6-7][0-9]/ {printf $2 "\t" $6 "\n"}’ #stuedent表中列为姓名,列6为成绩
#输出:
张三 66
李四 77
#输出成绩大于在60~80分之间的学生姓名和成绩
参数:
- -F 指定输入分隔符
# 以:作为输入的列分隔符,查看admin用户的UID
cat /etc/passwd |grep admin |awk -F':' '{print $3}'
- -f 从脚本读取awk命令
#脚本pass.awk
BEGIN {FS=":"}
{print $1 "\t" $3}
# awk调用脚本
awk -f pass.awk /etc/passwd
- -m 对val值设置内在限制
- -V 自定义变量
# 输出aA的值kkk
awk -v aA="kkk" 'BEGIN{print aA}'
再看几个内置变量
参考:https://blog.csdn.net/w918589859
awk内置变量 | 作用 |
---|---|
$0 | 代表目前awk所读入的整行数据。我们已知awk是一行一行读入数据的,$0就代表当前读入行的整行数据。 |
$n | 代表目前读入行的第n个字段。比如,$1表示第1个字段(列),$2表示第2个字段(列),如此类推 |
NF | 当前行拥有的字段(列)总数。 |
NR | 当前awk所处理的行,是总数据的第几行。 |
FS | 用户定义分隔符。awk的默认分隔符是任何空格,如果想要使用其他分隔符(如“:”),就需要FS变量定义。 |
ARGC | 命令行参数个数。 |
ARGV | 命令行参数数组。 |
FNR | 当前文件中的当前记录数(对输入文件起始为1)。 |
OFMT | 数值的输出格式(默认为%.6g)。 |
OFS | 输出字段的分隔符(默认为空格)。 |
ORS | 输出记录分隔符(默认为换行符)。 |
RS | 输入记录分隔符(默认为换行符)。 |
以上变量的解释参考:https://blog.csdn.net/qq_40531442
注意:这个例子的输出怀疑有问题,不应该以#分割
另外补几个示例:
参考:https://blog.csdn.net/w918589859
#打印文本第一行
awk 'NR==1{print}' filename
#打印文本第二行第一列
sed -n "2, 1p" filename | awk 'print $1'
#获取第一行的第一列,第二列,第三列
ps -aux | grep watchdog | awk 'NR==1{print $1, $2, $3}'
#获取行数NR
df -h | awk 'END{print NR}'
#获取列数NF(这里是获取最后一行的列数,注意每行的列数可能是不同的)
ps -aux | grep watchdog | awk '{print $NF}'
#统计PHIP成绩的总分
[root@localhost ~]$ awk 'NR==2 {php1=$3}
NR==3 {php2=$3}
NR==4 {php3=$3;totle=phpl+php2+php3;print "totle php is " totle}' student.txt
#假设如果Linux成绩大于90,就是一个好男人
[root@localhost ~]$ awk '{if (NR>=2) {if ($4>60) printf $2 "is a good man!\n"}}’ student.txt
[root@localhost ~]$ awk "NR>=2 {test=$4}
test>90 {printf $2 "is a good man! \n"}" student.txt
#
几个awk书写格式小tips:
- 多个条件 {动作} 可以用空格分割,也可以用回车分割。
- 在一个动作中,如果需要执行多个命令,需要用 “;” 分割,或用回车分割。
- 在awk中,除了$0 $n变量的赋值与调用都不需要加入“$”符。
- 条件中判断两个值是否相同,请使用 “==”,以便和变量赋值进行区分。