preparing

查看当前操作系统的默认shell echo $SHELL

vimrc的推荐配置(非必须)

set nu
set cursorline
set autoindent
autocmd BufNewFile *.sh 0r ~/.vim/template/sh —>创建新文件的头文件

1
2
3
4
5
cd ..;ls    #进入上一级目录再执行ls,结束后wd位于..
(cd ..;ls) #进入上一级目录再执行ls,结束后wd位于.(fork,exec)
#内建命令&外部命令(通过环境变量)
#查看环境变量
env $path

说明:在学习时,以下提到的命令和demo大多可直接在shell中运行,无需编写成shell文件

第一个shell文件(vim sample.sh)

1
2
3
4
5
6
#!/bin/bash       #指定解析器
#仅支持单行注释,多行可以通过语句块实现
date
echo ""
echo "we are current in following path"
/bin/pwd

shell的运行(三种)

  1. chmod +x sample.sh ./sample.sh
  2. . sample.sh
  3. bash sample.sh

数据类型和变量

shell中只有字符串类型,有两种变量类型: 环境变量(类似于全局变量) 本地变量(类似于局部变量)

1
2
3
4
5
6
7
8
9
#声明变量
var=10 #不能有空格
#查看变量的值
echo $var #或 echo ${var} [推荐]
#删除变量
unset var
#控制语句
if,else,switch case,for,while...
#函数

匹配&代换

匹配

* 匹配0或任意个字符

? 匹配一个任意字符

[若干字符] 匹配方括号中的任意一个字符的一次出现 ([a-z])

命令代换

1
2
now=$(date)		#将date命令运行的结果赋值给now变量等价于以下命令:
now=`date`

算术代换

使用$(())用于算术运算,(())中的变量取值将转换到整数,功能同 $[]

1
2
3
4
5
var=10
echo $(($var+5)) #15 == echo $[$var+5] == echo $[$var+5] == echo $((${var}+5))
#注:只能进行+1*/和()运算且只能进行整数运算。
#进制
echo $[2#11+3] #二进制的"11" + 3

转义字符

shell使用 “\“ 作为转义字符,用于去除紧跟其后的一个字符的特殊含义,即紧跟其后的字符取字面值[转特殊义或转本身义,参考echo参数节]

1
2
3
echo \$path #----->   $path
#创建 名为 --abc 的文件
touch -- --abc

单双引号

单双引号内的内容都将被视为单一字符串,区别在于 双引号阻止通配符的扩展但允许变量扩展

1
2
3
var=hello
echo "$var" #==> hello
echo '$var' #==> $var

条件测试

条件测试结果 —> 真:0 假:1 (类似于unix的syscall)

算术比较符(greater,less than…):

arg1 op arg2 (只能为整数)

-gt 大于 -ge 大于等于 -eq 等于

-lt 小于 -le 小于等于 -ne 不等于

1
2
3
var=10
test $var -gt 11 #===>等价于 [ $var -gt 11 ] 空格不能少(相当于[是运算符)
echo $? #查看上一次命令运行的结果

文件类型比较符

-d arg 存在且是一个目录为真 -f arg 存在且是一个文件为真 -p arg 存在且是一个管道为真 -l arg软连接 -c arg 字符设备 -b arg 块设备 -s socket文件

1
2
3
4
# 当前目录下存在一个名为 go 的文件夹
[ -d go ] #===>echo $? --->0
mkfifo ww
[ -p ww ] #===>echo $? --->1

逻辑运算

​ -a 逻辑与 -o 逻辑或 ! 逻辑非

1
2
expr1 -a expr2
! expr

字符串比较符

-z 如果字符串的长度且为0则为真,-n 如果字符串长度不为0为真

判断字符串相等/不等 == ,!=

1
2
3
4
5
6
7
8
9
10
11
12
#变量不存在时(!!!注意变量前的$不能少):
unset var
[ -z $var ] #===>echo $? --->0
[ -n $var ] #===>echo $? --->0
var="abc"
[ -z $var ] #===>echo $? --->1
[ -n $var ] #===>echo $? --->0
#-----
var="abc"
[ $var = "abc" ] #echo $? --->0##!!!!注意等号两边的空格不能少
[ $var = "abcd" ]#echo $? --->1
[ $var != "abcd" ]#echo $? --->0

补充

​ 条件测试也除了使用以上的[]形式,也可以使用test命令,推荐使用[]

​ 未定义的变量在shell中会被展开为空( 空而不是”” ),因此良好的shell编程习惯中推荐总是使用双引号来对变量取值,比如以下例子:

1
2
3
4
#变量vr不存在
var="abc"
[ $vr != "abcd" ] #err,报错
[ "$vr" != "abcd" ] #返回0

if分支

demo1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cd /
if [ -d bin ];then
> echo "It's a dictory"
> fi
It's a dictory
###
#! /bin/bash
if [ -f sample.sh ];then
echo "It's a file"
fi
###等价于
if [ -f sample.sh ]
then
echo "It's a file"
fi

demo2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#! /bin/bash

echo "Is it morning? please enter y or n:"
read ord
if [ $ord = "y" ];then
echo "good morning"
elif [ $ord = "n" ]; then
echo "good night"
else
echo "what black man question mark?"
exit 1
fi
if :; then
echo ": is always true" ### : 表示一条空指令且总为true
if

switch case

1
2
3
4
5
6
7
8
9
10
11
12
13
#说明:无switch,使用 ;; 防止case穿透(相当于break)
#! /bin/bash

echo "Is it morning? please enter yes or no:"
read ord
case "$ord" in
[yY][eE][Ss])
echo "good mao ni";;
[Nn][Oo])
echo "good evening";;
*)
echo "?????"
esac

循环

for

1
2
3
4
5
6
#! /bin/bash

for i in `ls`;do
echo "file name is $i,more about:"
ls -al $i
done

while

demo1
1
2
3
4
5
6
7
#! /bin/bash
echo "please enter the passwd"
read passwd
while [ "$passwd" != "secret" ];do
echo "sorry,passwd is not match,try again"
read passwd
done
demo2
1
2
3
4
5
6
7
8
#! /bin/sh

echo "while loop"
count=0
while [ "$count" -lt 10 ];do
echo "now it is $count"
count=$(($count+1))
done

break[n]可以跳出多层循环,continue同C语言相同。

demo2改进
1
2
3
4
5
6
7
8
9
10
11
#! /bin/sh

echo "while loop"
count=0
while [ "$count" -lt 10 ];do
echo "now it is $count"
count=$(($count+1))
if [ "$count" -eq 7 ];then
break
fi
done

命令行参数

1
2
3
4
5
6
7
8
9
10
11
12
##文件名为 argv.sh
#! /bin/bash
echo $0
echo $1
echo $2
echo $3
echo $4
echo "the argv's number is $#"
# $@为参数列表,可用于for循环
# $$ 当前进程的进程号
# $? 上一条命令的Exit status
# shift,命令行参数左移,自己测试(eg:处理一个左移一次)

bash argv.sh a1 a2 a3 a4 —>查看结果

也可以 chmod +x argv.sh,./argv a1 a2 a3 a4 查看结果

echo,printf,tee,重定向

-e 解析转义字符

​ 运行以下命令查看区别:echo "hello\n"echo -e "hello\n"

-n 不回车换行

​ 运行以下命令查看区别:echo "hello"echo -n "hello"

printf

​ printf命令有和echo相似的功能,具体使用请参考man

管道: 通过 | 把一个命令的输出传递到另一个命令做输入

tee

1
2
3
4
5
#>>代表在文件后面追加,不覆盖    > 代表覆盖文件内容写入
#当我们使用想将一个命令运行的结果输出到一个文件时
ls > out #此时屏幕上将不能显示运行结果,此时可以使用tee命令(输出到屏幕并保存到文件)
ls | tee out #此时将覆盖文件
ls | tee -a out #追加

重定向

1
2
3
4
5
6
7
cmd  > file  #标准输出重定向到新文件中
cmd >> file #标准输出重定向追加到文件中
cmd >file 2>&1 #标准出错也重定向到file文件中
cmd >>file 2>&1
cmd <file >fiel2 #标准输入/输出都重定向到文件中(cat < file )
cmd < &- #关闭标准输出
cmd >&fd #重定向到文件描述符中

由重定向引出来的两个关于nohup的非常常用的命令(nohup输出日志文件非常占用空间)

nohup commond >/dev/null 2>&1 & 不保存任何信息

nohup commond >/dev/null 2>log & 只输出错误信息到日志文件

函数

shell中的函数无返回值也无参数列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#定义函数
foo(){
echo "foo"
}
#调用
foo
#-------------传参demo,类似命令行参数-------
#! /bin/bash
foo(){
echo "$0"
echo "$1"
echo "$@"
}

foo 1 2 3

shell脚本的调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-n 			#不执行,只检查语法(类似于nginx -t/ make -n)
-v #一边执行一边将出错打印出来
-x #跟踪程序执行信息,将每一条命令和执行结果输出出来
##
[root@iz2zeh46wfpirjtbp5x1xyz shell]# cat func3.sh
#! /bin/bash
echo "haha"
echo "ww"

[root@iz2zeh46wfpirjtbp5x1xyz shell]# bash -x func3.sh
+ echo haha
haha
+ echo ww
ww
#####部分片段
set -x #开启
echo "1"
echo "2"
...
set +x #关闭
#这样即可只对脚本的一部分片段进行追踪调试

正则表达式

扩展正则和基本正则

​ Basic正则与扩展正则类似,只是字符?+{}|()应解释为普通字符,如要表示上述特殊含义则需要加\转义

​ 如果使用grep而不是egrep并且不加-E参数则遵照Basic正则来解释。

grep

grep -r “key word” 快速搜索目录下的关键字

….

find

find命令常用来找文件

find / -name “*init” 从根目录下查找名字以”init”结尾的文件

find ./ -size +2k 从当前目录下查找大于2K文件

find ./ -size +20M -size -40M 从当前目录下查找大于20M小于40M文件 (一个size一个限制)

(无单位默认为扇区大小,0.5K)

find ./ -type f 从当前目录下寻找类型为文件的 -type f,b,s

find ./ -maxdepth 1 -name “*” 列出当前文件夹下的文件/目录(不递归,且此参数应放在前边)

find命令不能和管道符同时使用,应使用exec替代

find ./ -maxdepth 1 -exec ls -l {} ; \;表示结束符

find ./ -maxdepth 1 -ok rm {} ; 交互版的exec,每次执行命令前询问y,n

find ./ -maxdepth 1 | xargs ls -ld 分批处理(内容过多时内存紧张,性能低等时)

​ xargs默认按照空格分隔,若返回结果中的有空格则不能正常运行

====>find ./ -maxdepth 1 -print0 |xargs -0 ls -lh

-atime|ctime|mtime: 天为单位(访问/修改/属性被修改 时间)

-amin|cmin|mmin

1
2
cd /var/log
find ./ -name "syslog*" -mtime +5 #5天前,,-5表示5天内

sed

​ 流编辑器(stream editor),由于sed与vi同起源于UNIX的ed,因此两者有很多相似。

sed 将输入的内容读入缓冲区进行处理并输出。(默认并不修改源文件)

基本用法:

sed option 'script' file1 file2 ... 或者 sed option -f scriptfile file1 file2 ...

–version –help

-n,–silence,–quiet sed在所有脚本指令执行完成后将自动打印模式空间中的内容,这些选项可以屏蔽自动打印。

sed "s/at/ta/" test ##从test读入缓冲区并把内容中的at替换为 ta(注意结束的/不能少

-e 允许多个脚本

-f 指定脚本文件

-i 直接在源文件中修改(慎用)

sed "s/at/ta/" test -i ##将test文件中的at替换为 ta

脚本的指令说明

>a , append    追加
>
>i,insert        插入
>
>d, delete    删除
>
>s, substitution 替换
1
2
sed "1,5d" test 	将从test文件中读入的内容的1-5行删除
sed "2a DY不会编程" test 将从test文件中读入的内容第2行后加入 DY不会编程

对于脚本文件格式为 /pattern/action,pattern表示正则表达式,action表示编辑操作。sed程序一行一行读入文件到缓冲区,如果某一行和pattern匹配则执行对应的action,如果一条命令没有pattern,则action处理每一行。

/pattern/p 打印符合pattern的行

/pattern/d 删除符合pattern的行

sed 's/bc/-&-' testfile 将testfile中的bc替换为 -bc-(即&代替bc)

awk

awk既可以进行行处理也可以进行列处理,但通常使用sed处理行。

awk缺省的行分隔符是换行,缺省的列分隔符是连续的空格和tab,但也可以自定义

ps aux|awk '{print $2}' 取进程号(打印第2列) $0表示全部

基本用法:类似于sed

1
2
3
4
5
6
7
8
[root@iz2zeh46wfpirjtbp5x1xyz tmp]# cat testfile 
productA 12
productB 76
prductC 21
[root@iz2zeh46wfpirjtbp5x1xyz tmp]# awk '$2<75 {printf "%s %s\n",$0,"reorder"} $2>=75 { printf "%s \n",$0}' testfile
productA 12 reorder
productB 76
prductC 21 reorder

说明:

关于sed和awk的内容有很多(awk甚至有自己的语言),这里只简单介绍,具体可参考相关手册。