执行脚本
#! /bin/sh
cd ..
ls
sh test.sh 或者 source test.sh
脚本文件加上可执行权限然后执行:
chmod a+x test.sh
./test.sh
命令执行
``或$()
$ echo `date`
2020年 02月 27日 星期四 19:37:25 CST
$ echo $(date)
2020年 02月 27日 星期四 19:37:40 CST
算术运算
$(())
$ echo $((11*11))
121
$[base#n],其中base表示进制,n按照base进制解释
$ echo $[10#121/11]
11
单引号与双引号
$ DATE=`date`
$ echo '$DATE'
$DATE
$ echo "$DATE"
2020年 02月 27日 星期四 19:45:32 CST
单引号里的内容会被认为是字符串,注意字符串中不能出现单引号,双引号中可以被通配符替换。
通配符
通配符 | 作用 |
---|---|
* | 匹配0个或多个任意字符 |
? | 匹配一个任意字符 |
[若干字符] | 匹配方括号中任意一个字符的一次出现 |
$ ls /dev/ttyS*
$ ls ch0?.doc
$ ls ch0[0-2].doc
$ ls ch[012] [0-9].doc
条件测试
test与[ ]作用相同
$ test -d Desktop
$ echo $?
0
注意[ ] 命令与参数之间都要有空格
[ -d /bin/bash ]
pi@qing:~/Pictures $ echo $?
1
值得注意的是,这里的Exit Status值与正常的逻辑值相反,Exit Status为0,则条件值为真,Exit Status为1,则条件值为假。
常用的条件测试 | 说明 |
---|---|
[ -d DIR ] | 如果DIR存在并且是一个目录则为真 |
[ -f FILE ] | 如果FILE存在且是一个普通文件则为真 |
[ -z STRING ] | 如果STRING的长度为零则为真 |
[ -n STRING ] | 如果STRING的长度非零则为真 |
[ STRING1 = STRING2 ] | 如果两个字符串相同则为真 |
[ STRING1 != STRING2 ] | 如果字符串不相同则为真 |
[ ARG1 OP ARG2 ] | ARG1和ARG2应该是整数或者取值为整数的变量 |
OP可以为-eq(等于), -ne(不等于),-lt(小于),-le(小于等于),-gt(大于),-ge(大于等于)
STRING1 = STRING2注意两个字符串之间的空格,STRING1=STRING2代表赋值
逻辑判断
逻辑判断 | 值 |
---|---|
[ ! EXPR ] | !表示逻辑反 |
[ EXPR1 -a EXPR2 ] | -a表示逻辑与 |
[ EXPR1 -o EXPR2 ] | -o表示逻辑或 |
条件分支语句
if/then/elif/else/fi
再次注意一遍,if命令的参数组成一条子命令,如果该子命令的Exit Status为0(表示真),则执行then后面的子命令,如果Exit Status非0(表示假),则执行elif、else或者fi后面的子命令。这里的真假01和一般的逻辑判断相反。
#! /bin/sh
echo "Is it morning? Please answer yes or no."
read YES_OR_NO
if [ "$YES_OR_NO" = "yes" ]; then
echo "Good morning!"
elif [ "$YES_OR_NO" = "no" ]; then
echo "Good afternoon!"
else
echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
exit 1
fi
exit 0
此外,Shell还提供了&&和||语法,和C语言类似,具有Short-circuit特性,很多Shell脚本喜欢写成这样:
test "$(whoami)" != 'root' && (echo you are using a non-privileged account; exit 1)
&&相当于“if…then…”,而||相当于“if not…then…”。&&和||用于连接两个命令,而上面讲的-a和-o仅用于在测试表达式中连接两个测试条件,要注意它们的区别,例如,
test "$VAR" -gt 1 -a "$VAR" -lt 3
test "$VAR" -gt 1 && test "$VAR" -lt 3
两种写法等价。
case
#! /bin/bash
echo 'Is it morming'
read YES_OR_NO
case YES_OR_NO in
yes|YES|y)
echo 'good morning';;
n*)
echo 'good aftenoon';;
*)
echo 'sorry';;
esac
exit 0
每个匹配分支可以有若干条命令,末尾必须以;;结束,执行时找到第一个匹配的分支并执行相应的命令,然后直接跳到esac之后,不需要像C语言一样用break跳出。
循环
for do done
#! /bin/bash
for VAR in dog cat rabbit; do
echo "I like $VAR"
done
while do done
#! /bin/bash
echo 'ENTER the password'
NUM=1
while [ $NUM -le 5 ]; do
read TRY
if [ $TRY = "secret" ]; then
echo 'login sucess'
break
else
NUM=$(($NUM+1))
echo 'try again'
fi
done
echo 'try too many'
break可以指定跳出几层循环,continue跳过本次循环步,没跳出整个循环。
位置参数和特殊变量
参数变量 | 解释 |
---|---|
$0 | 相当于C语言main函数的argv[0],一般为运行名称 |
$1、$2… | 位置参数(Positional Parameter),按照传递顺序来 |
$# | 传参的个数 |
$@ | 表示参数列表"$1" “$2” …,例如可以用在for循环中的in后面。 |
$* | 表示参数列表"$1" “$2” …,同上 |
$? | 上一条命令的Exit Status |
$$ | 当前进程号 |
$ cat < practice2
#! /bin/bash
echo "$#"
echo "$0"
echo "$1"
echo "$2"
echo "$3"
echo "$@"
$ source practice2 aa bb
2
-bash
aa
bb
aa bb
常用基本命令
echo
echo [option] string
-e 解析转义字符
-n 不回车换行。默认情况echo回显的内容后面跟一个回车换行。
$ echo -e 'hello\n\n'
hello
$ echo 'hello\n\n'
hello\n\n
$ echo -n 'hello\n\n'
hello\n\n$
管道|
可以通过管道把一个命令的输出传递给另一个命令做输入。管道用竖线表示。
tee
tee命令把结果输出到标准输出,另一个副本输出到相应文件。
[qing@qing hangbao]$ df -h | sort -n
devtmpfs 990M 0 990M 0% /dev
/dev/vda1 40G 3.4G 35G 9% /
Filesystem Size Used Avail Use% Mounted on
tmpfs 1000M 0 1000M 0% /dev/shm
tmpfs 1000M 0 1000M 0% /sys/fs/cgroup
tmpfs 1000M 113M 887M 12% /run
tmpfs 200M 0 200M 0% /run/user/0
tmpfs 200M 0 200M 0% /run/user/1000
文件重定向
cmd | |
---|---|
cmd > file | 把标准输出重定向到新文件中 |
cmd >> file | 追加 |
cmd > file 2>&1 | 标准出错也重定向到1所指向的file里 |
cmd >> file 2>&1 | |
cmd < file1 > file2 | 输入输出都定向到文件里 |
cmd < &fd | 把文件描述符fd作为标准输入 |
cmd > &fd | 把文件描述符fd作为标准输出 |
cmd < &- | 关闭标准输入 |
nohup sh test.sh >test.log 2>&1 &
函数
#! /bin/sh
foo(){ echo "Function foo is called";}
echo "-=start=-"
foo
echo "-=end=-"
注意函数体的左花括号’{‘和后面的命令之间必须有空格或换行,如果将最后一条命令和右花括号’}'写在同一行,命令末尾必须有;号。
#! /bin/bash
is_directory()
{
if [ -d $1 ]; then
return 0
else
return 1
fi
}
for DIR in $@; do
if is_directory $DIR; then
echo "$DIR is exiting"
else
echo 'Creating now'
mkdir $DIR > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "create $DIR success"
else
echo "create $DIR failure"
fi
fi
done
在这里插入代码片
Shell脚本的调试方法
-n | 读一遍脚本中的命令但不执行,用于检查脚本中的语法错误 |
-v | 一边执行脚本,一边将执行过的脚本命令打印到标准错误输出 |
-x | 提供跟踪执行信息,将执行的每一条命令和结果依次打印出来 |
使用这些选项有三种方法,一是在命令行提供参数
sh -x ./script.sh
二是在脚本开头提供参数
#! /bin/sh -x
第三种方法是在脚本中用set命令启用或禁用参数
#! /bin/sh
if [ -z "$1" ]; then
set -x
echo "ERROR: Insufficient Args."
exit 1
set +x
fi
set -x和set +x分别表示启用和禁用-x参数,这样可以只对脚本中的某一段进行跟踪调试。
常用命令
find
一般格式
find pathname -options [-print -exec -ok ...]
-options | 解释 |
---|---|
-name | 按照文件名查找文件。 |
-perm | 按照文件权限来查找文件。 |
-prune | 使用这一选项可以使find命令不在当前指定的目录中查找,如果同时使用-depth选项,那么-prune将被find命令忽略。 |
-user | 按照文件属主来查找文件。 |
-group | 按照文件所属的组来查找文件。 |
-mtime -n +n | 按照文件的更改时间来查找文件,-n表示文件更改时间距现在n天以内,+n表示文件更改时间距现在n天以前。find命令还有-atime和-ctime 选项,但它们都和-m time选项。 |
-nogroup | 查找无有效所属组的文件,即该文件所属的组在/etc/groups中不存在。 |
-nouser | 查找无有效属主的文件,即该文件的属主在/etc/passwd中不存在。 |
-newer file1 ! file2 | 查找更改时间比文件file1新但比文件file2旧的文件。 |
-type | 查找某一类型的文件, b - 块设备文件。 d - 目录。 c - 字符设备文件。 p - 管道文件。 l - 符号链接文件。f - 普通文件。 |
-size n:[c] | 查找文件长度为n块的文件,带有c时表示文件长度以字节计。 |
-depth | 在查找文件时,首先查找当前目录中的文件,然后再在其子目录中查找。 |
-fstype | 查找位于某一类型文件系统中的文件,这些文件系统类型通常可以在配置文件/etc/fstab中找到,该配置文件中包含了本系统中有关文件系统的信息。 |
-mount | 在查找文件时不跨越文件系统mount点。 |
-follow | 如果find命令遇到符号链接文件,就跟踪至链接所指向的文件 |
mtime扩展 | |
---|---|
-amin n | 查找系统中最后N分钟访问的文件 |
-atime | n 查找系统中最后n*24小时访问的文件 |
-cmin n | 查找系统中最后N分钟被改变文件状态的文件 |
-ctime n | 查找系统中最后n*24小时被改变文件状态的文件 |
-mmin n | 查找系统中最后N分钟被改变文件数据的文件 |
-mtime n | 查找系统中最后n*24小时被改变文件数据的文件 |
可选参数 | |
---|---|
-exec: | find命令对匹配的文件执行该参数所给出的shell命令。相应命令的形式为’command’ {} \; |
-ok: | 和-exec的作用相同,只不过以一种更为安全的模式来执行该参数所给出的shell命令,在执行每一个命令之前,都会给出提示,让用户来确定是否执行。 |
示例:
1,-name
find ~ -name "*.sh" -print
2,在/var/log目录中查找更改时间在5日以前的文件并删除它们
find /var/log -name "messages-*" -mtime +5 -ok rm {} \;
注意空格和分号
3,在当前目录下查找文件权限位为755的文件,即文件属主可以读、写、执行,其他用户可以读、执行的文件
find . -perm 755 -print
4,按文件属主查找文件
find ~ -user qing -print
5,为了查找系统中所有文件长度为0的普通文件,并列出它们的完整路径;
$ find / -type f -size 0 -exec ls -l {} \;
按照文件长度来查找文件,这里所指的文件长度既可以用块(block)来计量,也可以用字节来计量。以字节计量文件长度的表达形式为N c;以块计量文件长度只用数字表示即可。
xargs
在使用find命令的-exec选项处理匹配到的文件时, find命令将所有匹配到的文件一起传递给exec执行。这里的exec的命令是一次性执行,当数量过大时,可能会出现内存溢出。
find . -type f -print | xargs file
用grep命令在所有的普通文件中搜索echo这个词
find . -type f -print | xargs grep "echo"
find . -name \* -type f -print | xargs grep "is" -n
grep
grep家族包括grep、egrep和fgrep。egrep和fgrep的命令只跟grep有很小不同。egrep是grep的扩展,支持更多的re元字符, fgrep就是fixed grep或fast grep,它们把所有的字母都看作单词,也就是说,正则表达式中的元字符表示回其自身的字面意义,不再特殊。linux使用GNU版本的grep。它功能更强,可以通过-G、-E、-F命令行选项来使用egrep和fgrep的功能。
-options | |
---|---|
-c: | 只输出匹配行的计数。 |
-i: | 不区分大小写。 |
-h: | 查询多文件时不显示文件名。 |
-l: | 查询多文件时只输出包含匹配字符的文件名。 |
-n: | 显示匹配行及 行号。 |
-s: | 不显示不存在或无匹配文本的错误信息。 |
-v: | 显示不包含匹配文本的所有行。 |
–color=auto : | 可以将找到的关键词部分加上颜色的显示。 |
pattern正则表达式主要参数: | |
---|---|
\: | 忽略正则表达式中特殊字符的原有含义。 |
^: | 匹配正则表达式的开始行。 |
$: | 匹配正则表达式的结束行。 |
<: | 从匹配正则表达 式的行开始。 |
>: | 到匹配正则表达式的行结束。 |
[ ]: | 单个字符,如[A]即A符合要求 。 |
[ - ]: | 范围,如[A-Z],即A、B、C一直到Z都符合要求 。 |
grep -E 与re正则表达式方法基本相同,可参考下面链接。
字符类
字符 | 含义 | 举例 |
---|---|---|
字符 | 含义 | 举例 |
. | 匹配任意一个字符 | abc.可以匹配abcd、abc9等 |
[ ] | 匹配括号中的任意一个字符 | [abc]d可以匹配ad、bd或cd |
- | 在[]括号内表示字符范围 | [0-9a-fA-F]可以匹配一位十六进制数字 |
^ | 位于[]括号内的开头,匹配除括号中的字符之外的任意一个字符 | [^xy]匹配除xy之外的任一字符,因此[^xy]1可以匹配a1、b1但不匹配x1、y1 |
数量限定符
字符 | 含义 | 举例 |
---|---|---|
? | 紧跟在它前面的单元应匹配零次或一次 | [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]+ |
{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地址 |
位置限定符
字符 | 含义 | 举例 |
---|---|---|
^ | 匹配行首的位置 | ^Content匹配位于一行开头的Content |
$ | 匹配行末的位置 | ;$匹配位于一行结尾的;号,^$匹配空行 |
< | 匹配单词开头的位置 | \<th匹配… this,但不匹配ethernet、tenth |
\> | 匹配单词结尾的位置 | p>匹配leap …,但不匹配parent、sleepy |
\b | 匹配单词开头或结尾的位置 | \bat\b匹配… at …,但不匹配cat、atexit、batch |
\B | 匹配非单词开头和结尾的位置 | \Bat\B匹配battery,但不匹配… attend、hat … |
grep -E 与re正则表达式方法基本相同,可参考下面链接。python中的正则表达式
sed
sed命令行的基本格式为
sed option 'script' file1 file2 ...
sed option -f scriptfile file1 file2 ...
常用选项含义: | |
---|---|
–version | 显示sed版本。 |
–help | 显示帮助文档。 |
-n,–quiet,–silent | 静默输出,默认情况下,sed程序在所有的脚本指令执行完毕后,将自动打印模式空间中的内容,这些选项可以屏蔽自动打印。 |
-e script | 允许多个脚本指令被执行。 |
-f script-file, --file=script-file | 从文件中读取脚本指令,对编写自动脚本程序来说很棒! |
-i,–in-place | 直接修改源文件,经过脚本指令处理后的内容将被输出至源文件(源文件被修改)慎用! |
-r, --regexp-extended | 在脚本指令中使用扩展正则表达式 |
-s, --separate | 默认情况下,sed将把命令行指定的多个文件名作为一个长的连续的输入流。而GNU sed则允许把他们当作单独的文件,这样如正则表达式则不进行跨文件匹配。 |
对与直接修改源文件,不建议这样做,可以利用tee创建副本。
sed ‘s/love/too’ | tee a.txt
简单运用
a,append 追加
i,insert 插入
d,delete 删除
s,substitution 替换
如
/pattern/p 打印匹配pattern的行
/pattern/d 删除匹配pattern的行
1, 在输出testfile内容的第二行后添加"love"
sed "2a itcast" testfile
2,删除2,5行
sed "2,5d" testfile
3,打印其中包含abc的行
sed '/abc/p' testfile
注意,sed是把待处理文件的内容连同处理结果一起输出到标准输出的,因此p命令表示除了把文件内容打印出来之外还额外打印一遍匹配pattern的行。
可以用-n来禁止输出,相当于grep
sed -n '/abc/p' testfile
替换的常见用法
sed /pattern/s/pattern1/pattern2/
#查找符合pattern的行,将该行第一个匹配pattern1的字符串替换为pattern2
sed /pattern/s/pattern1/pattern2/g
#查找符合pattern的行,将该行所有匹配pattern1的字符串替换为pattern2
1,
$ cat testfile
123
abc
456
$ sed 's/abc/~&~/' testfile
123
~abc~
456
pattern2中的&表示原文件的当前行中与pattern1相匹配的字符串
2,
[qing@qing hangbao]$ sed -r 's/([0-9]).([0-9])/~\1~\2~/' testfile
~1~3~
abc
~4~6~
pattern2中的\1表示与pattern1的第一个()括号相匹配的内容,\2表示与pattern1的第二个()括号相匹配的内容。
3,去掉所有的HTML标签
$ cat testfile
<html><head><title>Hello World</title></head>
<body>Welcome to the world of regexp!</body></html>
$ sed -r 's/<[^>]+>//g' testfile
Hello World
Welcome to the world of regexp!
4,
sed -r '/[a-z0-9-]{36}/s/[a-z0-9-]{35}[a-z0-9]/love/g' testfile
awk
sed以行为单位处理文件,awk比sed强的地方在于不仅能以行为单位还能以列为单位处理文件。awk缺省的行分隔符是换行,缺省的列分隔符是连续的空格和Tab,但是行分隔符和列分隔符都可以自定义,
命令的基本形式
awk option 'script' file1 file2 ...
awk option -f scriptfile file1 file2 ...
常见用法
/pattern/{actions}
condition{actions}
和sed类似,pattern是正则表达式,actions是一系列操作。awk程序一行一行读出待处理文件,如果某一行与pattern匹配,或者满足condition条件,则执行相应的actions,如果一条awk命令只有actions部分,则actions作用于待处理文件的每一行。
1,打印第二列
awk '{print $2;}' testfile
2,如果某种产品的库存量低于75则在行末标注需要订货
$ cat testfile
ProductA 30
ProductB 76
ProductC 55
$ awk '$2<75 {printf "%s\t%s\n" ,$0, "order";} $2>=75 {printf "%s\n" ,$0;}' testfile
ProductA 30 order
ProductB 76
ProductC 55 order
3,统计一个文件中的空行数
$ awk '/^ *$/ {x=X+1} END {print x};' practice2
1
awk常用的内建变量
变量名 | |
---|---|
FILENAME | 当前输入文件的文件名,该变量是只读的 |
NR | 当前行的行号,该变量是只读的,R代表record |
NF | 当前行所拥有的列数,该变量是只读的,F代表field |
OFS | 输出格式的列分隔符,缺省是空格 |
FS | 输入文件的列分融符,缺省是连续的空格和Tab |
ORS | 输出格式的行分隔符,缺省是换行符 |
RS | 输入文件的行分隔符,缺省是换行符 |
1,打印系统中的用户帐号列表
awk 'BEGIN {FS=":"} {print $1;}' /etc/passwd
2,
nova list | awk 'BEGIN {FS="|"} {print $3}'
习题
- 求2个数之和
- 计算1-100的和
#! /bin/bash
x=1
R=0
while [ $x -le 100 ]; do
R=$(($x+$R))
x=$(($x+1))
done
echo "$R"
exit 0
- 将一目录下所有的文件的扩展名改为bak
- 编译当前目录下的所有.c文件
#! /bin/bash
for file in *.c;do echo %file ; gcc -o $(basename $file .c) $file ; sleep 2; done \
> compile 2>&1
- 打印root可以使用可执行文件数,处理结果: root’s bins: 2306
echo "root'a bins:`find /root -user root | xargs ls -l | sed '/-..x/p' | wc -l `"
这里指的是/root下拥有执行权限的文件
- 打印当前sshd的端口和进程id,处理结果: sshd Port&&pid: 22 5412
netstat -apn | grep sshd | sed -n -r 's/.*:::([0-9]+).*([0-9]+)\/sshd/\1 \2/p'
- 输出本机创建20000个目录所用的时间,处理结果:
#! /bin/bash
time(
for i in {1..20000}; do
mkdir temp/nn$i
done
)
- 打印本机的交换分区大小,处理结果: Swap:1024M
free -m | grep Swap | awk '{print $2}'
- 文本分析,取出/etc/password中shell出现的次数
4 /bin/bash
1 /bin/sync
1 /sbin/halt
31 /sbin/nologin
1 /sbin/shutdown
cat /etc/passwd | awk -F: '$7!="" { print $7}' | sort | uniq -c
- 文件整理,employee文件中记录了工号和姓名,(提示join)
employee.txt:
100 Jason Smith
200 John Doe
300 Sanjay Gupta
400 Ashok Sharma
bonus文件中记录工号和工资
bonus.txt:
100 $5,000
200 $500
300 $3,000
400 $1,250
要求把两个文件合并并输出如下,处理结果:
400 ashok sharma $1,250
100 jason smith $5,000
200 john doe $500
300 sanjay gupta $3,000
join employee.txt bonus.txt | sort -k 2
- 编写个shell脚本将当前目录下大于10K的文件转移到/tmp目录下
#! /bin/bash
for FILE in `ls -l | awk ' $5>10240 {print $9}'`; do
mv $FILE /temp
done
ls -al /tmp
- 查看TCP连接状态
netstat -nat |awk '{print $6}' |sort|uniq -c|sort -rn
- 查找请求数请20个IP(常用于查找攻来源):
netstat -anlp|grep 80|grep tcp|awk '{print $5}' |awk -F: '{print $1}'|sort|uniq -c|sort -nr|head -n20
- 获得访问前10位的ip地址
cat access.log|awk '{print $1}'|sort|uniq -c|sort -nr|head -10
文本处理类的命令:
-
wc [option] [file]…
-l: 统计行数
-c: 统计字节数
-w;统计单词数 -
tr: 转换字符或删除字符
tr ‘集合1’ ‘集合2’
tr -d ‘字符集合’ -
sort [option] file…
-f: 忽略字符大小写;
-n: 比较数值大小;
-t: 指定分隔符
-k: 指定分隔后进行比较字段
-u: 重复的行,只显示一次; -
uniq
移除重复的行
-c:显示每行重复的次数
-d:仅显示重复过的行
-u: 仅显示不曾重复的行
工具速查链接http://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/index.html