shell笔记
1、shell基础
命令与逻辑语言共存,类似于C语言
&& ||具有逻辑判断, &&是前一个命令失败,后面不会执行,||是前一个命令失败,后面命令若能执行则执行。
;不影响命令执行
`mkdir /home/1/2/3 &&echo "OK"`#两条命令都不会执行
`mkdir -p /home/1/2/3 &&echo "OK"`#会显示ok,因为-p表示如果没有目录会自动创建,-p表示递归创建
输出命令
echo 内容
echo 输出颜色文本
echo -e "\e[1;31m this is text.\e[0m"
31-37文字颜色,41-47文字背景颜色
echo -e "\e[1;31m \e[1;42m this is text.\e[0m"
可以连用,不加尾部\e[0m表示以后输出一直为此颜色
命令后跟&
& 的作用就是将这个任务放到后台执行
command &>/dev/null
混合重定向到无底洞
通配符
- *表示匹配任意多个字符 如:
ls aa * rm -rf *;
- ?表示匹配任意单个字符
touch love loove live l7ve;ll l?ve
- []匹配括号中的任意一个字符
[abc]
,[^a-Z]
^表示取反,正则表达式内容,除括号以外的内容 - ()在子shell中执行
(cd /boot;ls)(umask 077;touch file1000)
- {}集合touch file{1…9}创建文件file1-file9,
mkdir /home/{111,222},mkdir -pv /home/{333/{aaa,bbb},444}
2、变量
shell变量使用一个特定的字符串表示不固定的内容,
$variable
引用变量的值
#!/bin/bash
ip=127.0.0.1 #本地还回
ping -c1 $ip &>/dev/null && echo "$ip is up " ||echo "$ip is down";
自定义变量
-
定义变量: 变量名=变量值 变量名必须是以字母或下划线开头,区分大小写。如:
ip=127.0.0.1
-
引用变量: $变量名 或${变量名}
-
查看变量: echo $变量名 set(所有变量:包括自定义变量和环境变量)
-
取消变量: unset变量名
-
作用范围: 仅在当前shell有效
#!/bin/bash
ip=127.0.0.1 #本地还回#变量和等号和值之间不能有空格#显示赋值
ping -c1 $ip &>/dev/null
if [ $? -eq 0 ];then#注意空格 ,否则出错
echo "ip is up"
else
echo "ip is down"
fi
环境变量
-
定义环境变量:
- 方法一 :
export back_dir2=/home/backup
- 方法二:
export back_dir1
将自定义变量转换成环境变量
- 方法一 :
-
引用环境变量: $变量名或者${变量名}
-
查看环境变量:echo $变量名, env , 例如env |grep back_dir2
- env显示所有环境变量
-
取消环境变量:unset变量名
-
变量作用范围:在当前shell和子shell有效
-
一个脚本用另个脚本里变量:
- 方法一:另一个脚本变量设为全局变量
- 方法二:在一个脚本里加载另一个脚本
#!/bin/bash
. 1.sh #v1在1.sh里#当前shell 执行#.和source都表示当前shell运行
echo -e "$v1"
位置变量
1.sh 1 2 3 4 5 6#$1 $2 $3 $4 ${5} 分别表示1 2 3 4 5
预定义变量
表达式 | 含义 |
---|---|
$0 | 脚本名 |
$* | 所有的参数,参数指脚本运行时后面的参数 |
$@ | 所有的参数 |
$# | 参数的个数 |
$$ | 当前进程的PID |
$! | 上一个后台进程的PID |
$? | 上一个命令的的返回值 0表示成功 |
#!/bin/bash
#如果脚本没有参数
if [ $# -eq 0 ]; then
echo -e "usage: `basename $0` file" #反引号,执行引号里面命令
exit
fi
if [ ! -f $1 ]; then#判断$1是不是常规文件,!取反
echo "error file"
exit
fi
for ip in `cat $1`
do
ping -c1 $ip &>/dev/null
if [ $? -eq 0 ];then #if后面有空格
echo "$ip is up"
else
echo "$ip is down"
fi
done
反引号,执行引号里面命令 basename 基本文件名 dirname 基本目录名
显示赋值
变量名=变量值
ip=127.0.0.1
school=“HDU_University”
today1=`date +%F`
today2=$(date +%F)
read从键盘读入变量值
-
read 变量名
-
read -p “提示信息:” 变量名
-
read -t 5 -p “提示信息:” 变量名 //等待5s时间
-
read -n k 变量名 //需要k个字符
“”,``与‘’的区别
-
""弱引用
-
''强引用
示例:
name=“mary” ;boy1=" n a m e i s g o o d " ; b o y 2 = ′ name is good";boy2=' nameisgood";boy2=′name is good’含义为boy1=mary is good,boy2=$name is good;
touch `date +%F`_file_txt ``表示执行``里面的命令在执行外面的
变量运算
变量运算方法1——expr
-
expr 1+2
-
expr $v1+$v2
-
expr $v1*$v2
变量运算方法2——$(())
-
echo ( ( (( ((v1+$v2))
-
echo $((v1+v2))
-
echo $((5-3))
-
echo $(((5-3)*2))
-
echo $((5**3)) //5的立方,n**k n的k方
-
sum=$((1+2))
变量运算方法3——$[]
和方法二一样
变量运算方法四let
-
let sum=2+3;
-
let i++
#!/bin/bash
ip=1.0.0.127
i=1
while [ $i -le 5 ] #-le 表示小于#记住[] 两头都要用 隔开
do
ping -c1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "$ip is up"
fi
let i++
done
整数运算
-
echo “2*4” |bc
-
echo “2^4” |bc
-
echo “scale=n;6/4” |bc //小数点后n位
bc表示计算
变量内容的删除和替换
删除变量的部分(本身不变)
前删
name=www.baidu.com
-
echo ${name#*.} 删除www.
-
echo ${name##*.}删除www.baidu.//贪婪匹配删除到最后一个.之前
后删
-
echo ${name%.*}删除.com
-
echo ${name%%.*}删除.baidu.com//贪婪匹配删除到最后一个.之前
切片
-
echo ${name:0:5}//取位置0开始后面5位
-
echo ${name:5:5}//取位置5开始后面5位
-
echo ${name:5}//取位置5后面所有
替换
-
echo ${name/baidu/sina}//把baidu替换为sina
-
echo ${name/b/B}//修改第一个字符b为B
-
echo ${name//w/W}//把所有w变成W
3.条件测试
test -d $v1 判断v1是不是目录 ,bash -vx .sh以调试的方式执行,test与[ ]一样
以下都可以通过命令man test查看
-
test条件表达式
-
[ 条件表达式 ], [ A 条件 B ]不能忽略空格,C风格可以
-
[[ 条件表达式 ]]
文件测试
格式 | 含义 |
---|---|
-r file | 当前用户是否有读权限 |
-L file | 文件是否是连接文件 |
-b file | 文件是否为设备文件 |
-c file | 文件是否为字符设备文件 |
-d file | 文件存在并且是一个目录 |
-e file | 文件已存在 |
-f file | 文件存在并且是常规文件 |
-g file | 文件已存在并设置为组ID |
-G file | 文件存在并且由有效组ID拥有 |
-h file | 文件存在并且是符号链接(与-L相同) |
-k file | 文件存在并设置了其粘滞位 |
-L file | 文件存在并且是符号链接(与-h相同) |
-O file | 文件存在并且由有效用户ID拥有 |
-p file | 文件存在并且是命名管道 |
-r file | 文件已存在并已授予读取权限 |
-s file | 文件存在且大小大于零 |
-S file | 文件存在并且是套接字 |
-u file | 文件存在并且其设置用户ID位已设置 |
-w file | 文件已存在并授予写入权限 |
-x file | 文件存在并授予执行(或搜索)权限 |
数值测试
INTEGER1 -eq INTEGER2 #等于
INTEGER1 is equal to INTEGER2
INTEGER1 -ge INTEGER2#大于等于
INTEGER1 is greater than or equal to INTEGER2
INTEGER1 -gt INTEGER2#大于
INTEGER1 is greater than INTEGER2
INTEGER1 -le INTEGER2#小于等于
INTEGER1 is less than or equal to INTEGER2
INTEGER1 -lt INTEGER2#小于
INTEGER1 is less than INTEGER2
INTEGER1 -ne INTEGER2#不等于
INTEGER1 is not equal to INTEGER2
C风格的数值判断
((C条件判断))
#!/bin/bash
#如果脚本没有参数
if [ $# -eq 0 ]; then
echo -e "usage: `basename $0` file" #反引号,执行引号里面命令
exit
fi
if [ ! -f $1 ]; then
echo "error file"
exit
fi
for ip in `cat $1`
do
ping -c1 $ip &>/dev/null
if (($?==0));then #if后面有空格
echo "$ip is up"
else
echo "$ip is down"
fi
done
字符串比较
-n STRING#字符串非空
the length of STRING is nonzero
STRING equivalent to -n STRING
-z STRING#字符串为空
the length of STRING is zero
STRING1 = STRING2#相等
the strings are equal
STRING1 != STRING2#不相等
the strings are not equal
表达式
( EXPRESSION )
EXPRESSION is true
! EXPRESSION
EXPRESSION is false
EXPRESSION1 -a EXPRESSION2
both EXPRESSION1 and EXPRESSION2 are true
EXPRESSION1 -o EXPRESSION2
either EXPRESSION1 or EXPRESSION2 is true
用户创建
#!/bin/bash
read -p "Please input the number of users: " number
if [[ ! "$number" =~^[0-9]+$ ]];then
echo "$number is not number."
fi
read -p "Please input a name for users: " string
for i in `seq $number`
do
user=$string$number
useradd $user
if [ $? -eq 0 ];then
echo "$user is created."
fi
done
4、流控、循环
if
if 条件测试
then
内容
elif 条件测试
then
内容
elif 条件测试
then
内容
else #注意此处没有then 加then是错误语法
内容
fi
case
case 变量 in
模式一)#加不加引号一样,匹配空字符时要加""
命令序列
;;#分号一定不能少
模式二)
命令序列
;;
模式三)
命令序列
;;
*)
无匹配的默认序列
esac
示例:删除用户
#!/bin/bash
#del user
#by dengwei
#date 2021.4.21
read -p "Please input a user: " user
id $user&>/dev/null
if [ $? -ne 0 ]; then
echo "username is not exist"
exit -1
fi
read -p "Are sure?[y]" action
case "$action" in
y|Y|yes|YES)
userdel -r $user
echo "success"
;;
"*")
echo "exit"
esac
简单测试
#!/bin/bash
#del user
#by dengwei
#date 2021.4.21
read -p "Please input a user: " user
read -p "Are sure?[y]" action
case "$action" in
y|Y|yes|YES)
echo "success"
;;
"*")
echo "exit"
esac
for(对文件逐行处理最好用while)
for 变量 in {取值列表}
do
命令序列
done
for 变量 #后面什么都不加表示获取所有参数
do
命令序列
done
示例:
#!/bin/bash
ip=www.baidu.com
for i in {1..100}
do
{//将循环放在后台加快速度{}&
ping -c1 $ip&>/dev/null
if [ $? -eq 0 ]; then
echo "$ip"
fi
}&
done
wait#等待前面所有后台结束
echo "finish"
{
}&#将循环放在后台加快速度{}&
IFS=“\n”按行分割 IFS修改分隔符
#!/bin/bash
IFS="\n"
if [ $# -eq 0 ];then
echo "please input a file"
exit -1
fi
if [ ! -f $1 ];then
echo "$1 isn't a file"
exit -1
fi
for i in `cat $1`
do
echo "$i"
done
while
while 条件测试
do
循环体
done#<file/$1等会输入重定向
条件测试为真时执行
until
until 条件测试
do
循环体
done
条件测试为假时执行
break(同C语言)
continue(同C语言)
exit(同C语言)
shift
将位置变量往左移动, shift n往左移动n个,并减少参数总共个数
./1.sh 1 2 3
脚本内部shift 1 一次,参数变为 2 3分别对应$1 $2
5、数组与函数
array
普通数组定义方式:
数组名=(v1 v2 v3 v4 v5 v6)
#索引 0 1 2 3 4 5
数组名=(v1 v2 v3 v4 v5 [20]=v6)
#索引 0 1 2 3 4 20
数组名[0]=v1
数组名[1]=v2
数组名[2]=v3
数组名[3]=v4
关联数组定义方式:类似哈希表
declar -A 数组名#此处需要提前声明为关联数组
数组名=([name]=jack [age]=18)
#索引 name age
function
两处注释一定要注意
函数名() {#内部出现的位置参数是函数调用时的参数
函数块
}
函数名 参数#此处出现的位置参数是脚本调用时的参数
fuction 函数名 {
函数块
}
函数名 参数
#!/bin/bash
Factorial() {
val=1
for i in `seq $1` #函数位置变量,$1指函数调用时的参数
do
val=$(($val*$i))
done
echo "$val"
}
result=`Factorial $1` #sh脚本位置变量,此处的$1指的是脚本的参数
echo "$result"
seq 8指的是1 2 3 4 5 6 7 8
#!/bin/bash
test(){
return $[2*$1]
}
test $1
res=$?
echo "$res"
return 返回的是程序或者函数运行完后的返回值,echo是函数运行完后输出想要的结果可以把此结果赋值给另一个变量来保存可以当返回值来用
把返回值存入数组
返回数组
#!/bin/bash
num=(4 5 6)
array() {
b=66 #b是全局变量
local c=66 #c是局部变量
for i in $@ #所有参数 #注释与代码间最好有空格,这样避免出错
do
val[j++]=$i
done
echo "${val[*]}"
}
#array ${num[*]}
#echo 可以输出b,不能输出c因为c是局部的
result=`array ${num[*]}` #nums所有值,所有元素
#echo 不能输出b,因为上述过程是在子shell执行的
echo "$result"
注释与代码间最好有空格,这样避免出错
6、正则表达式、grep与awk
正则表达式基本语法
字符类
字符 含义 举例
. 匹配任意一个字符 abc.可以匹配abcd、abc9等
[] 匹配括号中的任意一个字符 [abc]d可以匹配ad、bd或cd
- 在[]括号内表示字符范围 [0-9a-fA-F]可以匹配一位十六进制数字
^ 位于[]括号内的开头,匹配除括号中的字符之外的任意一个字符 [^xy]匹配除xy之外的任一字符,因此[^xy]1可以匹配a1、b1但不匹配x1、y1
[[:xxx:]] grep工具预定义的一些命名字符类 [[:alpha:]]匹配一个字母,[[:digit:]]匹配一个数字
数量限定符
字符 含义 举例
? 紧跟在它前面的单元应匹配零次或一次 [0-9]?\.[0-9]匹配0.0、2.3、.5等,由于.在正则表达式中是一个特殊字符,所以需要用\转义一下,取字面值
+ 紧跟在它前面的单元应匹配一次或多次 [a-zA-Z0-9_.-]+@[a-zA-Z0-9_.-]+\.[a-zA-Z0-9_.-]+匹配email地址
* 紧跟在它前面的单元应匹配零次或多次 [0-9][0-9]*匹配至少一位数字,等价于[0-9]+,[a-zA-Z_]+[a-zA-Z_0-9]*匹配C语言的标识符
{N} 紧跟在它前面的单元应精确匹配N次 [1-9][0-9]{2}匹配从100到999的整数
{N,} 紧跟在它前面的单元应匹配至少N次 [1-9][0-9]{2,}匹配三位以上(含三位)的整数
{,M} 紧跟在它前面的单元应匹配最多M次 [0-9]{,1}相当于[0-9]?
{N,M} 紧跟在它前面的单元应匹配至少N次,最多M次 [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}匹配IP地址
再次注意grep找的是包含某一模式的行,而不是完全匹配某一模式的行。再举个例子,如果文本文件的内容是
aaabc
aad
efg
查找a*这个模式的结果是三行都被找出来了
$ egrep 'a*' testfile
aabc
aad
efg
a匹配0个或多个a,而第三行包含0个a,所以也包含了这一模式。单独用a这样的正则表达式做查找没什么意义,一般是把a*作为正则表达式的一部分来用。
位置限定符
字符 含义 举例
^ 匹配行首的位置 ^Content匹配位于一行开头的Content
$ 匹配行末的位置 ;$匹配位于一行结尾的;号,^$匹配空行
\< 匹配单词开头的位置 \<th匹配... this,但不匹配ethernet、tenth
\> 匹配单词结尾的位置 p\>匹配leap ...,但不匹配parent、sleepy
\b 匹配单词开头或结尾的位置 \bat\b匹配... at ...,但不匹配cat、atexit、batch
\B 匹配非单词开头和结尾的位置 \Bat\B匹配battery,但不匹配... attend、hat ...
位置限定符可以帮助grep更准确地查找,例如上一节我们用[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}查找IP地址,找到这两行
192.168.1.1
1234.234.04.5678
如果用^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$查找,就可以把1234.234.04.5678这一行过滤掉了。
其它特殊字符
字符 含义 举例
\ 转义字符,普通字符转义为特殊字符,特殊字符转义为普通字符 普通字符<写成\<表示单词开头的位置,特殊字符.写成\.以及\写成\\就当作普通字符来匹配
() 将正则表达式的一部分括起来组成一个单元,可以对整个单元使用数量限定符 ([0-9]{1,3}\.){3}[0-9]{1,3}匹配IP地址
| 连接两个子表达式,表示或的关系 n(o|either)匹配no或neither
以上介绍的是grep正则表达式的Extended规范,Basic规范也有这些语法,只是字符?+{}|()应解释为普通字符,要表示上述特殊含义则需要加\转义。如果用grep而不是egrep,并且不加-E参数,则应该遵照Basic规范来写正则表达式。
grep
1.格式
grep [options]
2.主要参数
grep --help
[options]主要参数:
-c:只输出匹配行的计数。
-i:不区分大小写。
-h:查询多文件时不显示文件名。
-l:查询多文件时只输出包含匹配字符的文件名。
-n:显示匹配行及 行号。
-s:不显示不存在或无匹配文本的错误信息。
-v:显示不包含匹配文本的所有行。
--color=auto :可以将找到的关键词部分加上颜色的显示。
pattern正则表达式主要参数:
\: 忽略正则表达式中特殊字符的原有含义。
^:匹配正则表达式的开始行。
$: 匹配正则表达式的结束行。
\<:从匹配正则表达 式的行开始。
\>:到匹配正则表达式的行结束。
[ ]:单个字符,如[A]即A符合要求 。
[ - ]:范围,如[A-Z],即A、B、C一直到Z都符合要求 。
.:所有的单个字符。
*:有字符,长度可以为0。
4.grep命令使用简单实例
$ grep ‘test’ d*
显示所有以d开头的文件中包含 test的行。
$ grep ‘test’ aa bb cc
显示在aa,bb,cc文件中匹配test的行。
$ grep ‘[a-z]\{5\}’ aa
显示所有包含每个字符串至少有5个连续小写字符的字符串的行。
$ grep ‘w\(es\)t.*\1′ aa
如果west被匹配,则es就被存储到内存中,并标记为1,然后搜索任意个字符(.*),这些字符后面紧跟着 另外一个es(\1),找到就显示该行。如果用egrep或grep -E,就不用”\”号进行转义,直接写成’w(es)t.*\1′就可以了。
5.grep命令使用复杂实例
明确要求搜索子目录:
grep -r
或忽略子目录:
grep -d skip
如果有很多输出时,您可以通过管道将其转到’less’上阅读:
$ grep magic /usr/src/Linux/Documentation/* | less
这样,您就可以更方便地阅读。
有一点要注意,您必需提供一个文件过滤方式(搜索全部文件的话用 *)。如果您忘了,’grep’会一直等着,直到该程序被中断。如果您遇到了这样的情况,按 ,然后再试。
下面还有一些有意思的命令行参数:
grep -i pattern files :不区分大小写地搜索。默认情况区分大小写,
grep -l pattern files :只列出匹配的文件名,
grep -L pattern files :列出不匹配的文件名,
grep -w pattern files :只匹配整个单词,而不是字符串的一部分(如匹配’magic’,而不是’magical’),
grep -C number pattern files :匹配的上下文分别显示[number]行,
grep pattern1 | pattern2 files :显示匹配 pattern1 或 pattern2 的行,
例如:grep "abc\|xyz" testfile 表示过滤包含abc或xyz的行
grep pattern1 files | grep pattern2 :显示既匹配 pattern1 又匹配 pattern2 的行。
grep -n pattern files 即可显示行号信息
grep -c pattern files 即可查找总行数
这里还有些用于搜索的特殊符号:
\< 和 \> 分别标注单词的开始与结尾。
例如:
grep man * 会匹配 ‘Batman’、’manic’、’man’等,
grep ‘\<man’ * 匹配’manic’和’man’,但不是’Batman’,
grep ‘\<man\>’ 只匹配’man’,而不是’Batman’或’manic’等其他的字符串。
‘^’:指匹配的字符串在行首,
‘$’:指匹配的字符串在行 尾,
awk
awk命令行的基本形式为:
awk option 'script' file1 file2 ...
awk option -f scriptfile file1 file2 ...
和sed一样,awk处理的文件既可以由标准输入重定向得到,也可以当命令行参数传入,编辑命令可以直接当命令行参数传入,也可以用-f参数指定一个脚本文件,编辑命令的格式为:
/pattern/{actions}
condition{actions}
和sed类似,pattern是正则表达式,actions是一系列操作。awk程序一行一行读出待处理文件,如果某一行与pattern匹配,或者满足condition条件,则执行相应的actions,如果一条awk命令只有actions部分,则actions作用于待处理文件的每一行。比如文件testfile的内容表示某商店的库存量:
ProductA 30
ProductB 76
ProductC 55
打印每一行的第二列:
$ awk '{print $2;}' testfile
30
76
55
自动变量$1、$2分别表示第一列、第二列等,类似于Shell脚本的位置参数,而$0表示整个当前行。再比如,如果某种产品的库存量低于75则在行末标注需要订货:
$ awk '$2<75 {printf "%s\t%s\n", $0, "REORDER";} $2>=75 {print $0;}' testfile
ProductA 30 REORDER
ProductB 76
ProductC 55 REORDER
可见awk也有和C语言非常相似的printf函数。awk命令的condition部分还可以是两个特殊的condition-BEGIN和END,对于每个待处理文件,BEGIN后面的actions在处理整个文件之前执行一次,END后面的actions在整个文件处理完之后执行一次。
awk命令可以像C语言一样使用变量(但不需要定义变量),比如统计一个文件中的空行数
$ awk '/^ *$/ {x=x+1;} END {print x;}' testfile
就像Shell的环境变量一样,有些awk变量是预定义的有特殊含义的:
awk常用的内建变量
FILENAME 当前输入文件的文件名,该变量是只读的
NR 当前行的行号,该变量是只读的,R代表record
NF 当前行所拥有的列数,该变量是只读的,F代表field
OFS 输出格式的列分隔符,缺省是空格
FS 输入文件的列分隔符,缺省是连续的空格和Tab
ORS 输出格式的行分隔符,缺省是换行符
RS 输入文件的行分隔符,缺省是换行符
例如打印系统中的用户帐号列表
$ awk 'BEGIN {FS=":"} {print $1;}' /etc/passwd