crontab -l #查看当前计划任务
crontab -e #自动打开vim,相当于创建计划任务
crontab -r #删除计划任务
crontab -u 用户名 #指定用户
service cron restart #重启cron服务
*表示取值范围内的数字
/表示每
-表示从某个数字到某个数字
,表示分开几个离散的数字
# 分 时 日 月 星
20 21 * * 1-5 /path/test.sh >> /path/test.log 2>&1
标准出错重定向到标准输出
* * * * * date >> ~/date.log
特点
1.解释非编译型
2.弱类型(不做类型区分,可以发生隐式类型转换)
3.执行模式:交互式/批处理式
C语言是静态弱类型语言 (静态:编译过程中变量类型不能被修改)
C++是静态强类型语言
shell脚本是动态弱类型语言
Python是动态强类型语言
eg: first.sh
#!/bin/bash
echo "hello world!"
#!是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种Shell。
通常,(#!)的名称,叫做”Shebang”或者”Sha-bang”。echo命令用于向窗口输出文本。
#后面加注释
执行方式
1. #chmod +x first.sh
#./first.sh
2. #/bin/bash first.sh
或者 #bash first.sh
这样执行的话就不需要在第一行指定解释器信息. /usr/bin/sh -> bash
eg:
pwd
cd ..
pwd
sh first.sh 虽然会打印出pwd的执行结果,但是cd ..到上个目录并没有被执行
sh创建bash父进程的子进程,在子进程切换目录后并没有把执行结果带回父进程。
source first.sh 回到上级目录 . first.sh同理
source直接使用父进程bash来操作,因此可以切换目录。
执行本质原理
第一种执行方式: chmod +x first.sh。Shell会fork一个子进程并调用exec执行./first.sh这个程序,exec系统调用应该把子进程
的代码段替换成./first.sh程序的代码段,并从它的_start开始执行。然而first.sh是个文本文件,根本没有代码段和_start函数!
怎么办呢? 其实exec还有另外一种机制,如果要执行的是一个文本文件,并且第一行用Shebang指定了解释器,则用解释器程序的代码段
替换当前进程,并且从解释器的_start开始执行,而这个文本文件被当作命令行参数传给解释器。
交互Shell(bash) fork/exec一个子Shell(sh)用于执行脚本,父进程bash等待子进程sh终止。sh读取脚本中的cd ..命令,
调用相应的函数执行内建命令,改变当前工作目录为上一级目录。sh读取脚本中的ls命令,fork/exec这个程序,
列出当前工作目录下的文件,sh等待ls终止。ls终止后,sh继续执行,读到脚本文件末尾,sh终止。sh终止后,bash继续执行,打印提示符等待用户输入。
变量(类似于宏,字符串替换)
定义变量时不加空格。
注意:
rm -rf /$mydir 一定要注意查看变量mydir里面存放了什么,否则就被认为是空串导致删除根目录。
变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界。 推荐给所有变量加上花括号。
字符串拼接时就必须加上花括号。
eg: echo ${mystr1}hello # 防止变量名产生歧义,加上{}
echo "$mystr1 $mystr2" # 打印对应的值
echo '$mystr1 $mystr2' # 单引号打印$mystr1 $mystr2
单引号不会对变量进行求值,双引号会对变量的内容进行求值。
只读变量
使用readonly命令可以让变量变为只读变量,只读变量的值不能被改变。
删除变量
unset命令不能删除只读变量。
变量的分类
本地变量: 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
环境变量: 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正
常运行。必要的时候shell脚本也可以定义环境变量。
shell变量: shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部
变量,这些变量保证了shell的正常运行。
获取字符串长度 #变量名
mystr="hehe"
echo ${#mystr}
提取子字符串
mystr="abcd1234"
echo ${mystr:1:4} # 从第二(相当于数组下标1)个字符开始提取4个字符bcd1
查找字符串
mystr="bit is a great company"
index=`echo $mystr | awk '{i=index($0, "great"); print i;}'` # $0表示第一个参数 从1开始计数 返回10就是g出现的下标
通配符
* : 匹配0个或多个任意字符
? : 匹配一个任意字符
[若干字符] : 匹配方括号中任意一个字符的一次出现(或的关系)
在参数还没传给程序之前已经展开了, 比如上述ls file[1-5]命令,如果当前目录下有file1~file5,
则传给ls命令的参数实际上是这5个文件名,而不是一个匹配字符串。
命令替换和算术替换
由反引号``括起来的也是一条命令,shell先执行该命令,然后将输出结果立刻替换到当前命令行中。
命令代换也可以用$() 表示: DATE=$(date +%Y:%m:%d)
(()) 中的shell变量取值将转换成整数,常用于算术计算 +-*/% ++ --和()运算,并且只能做整数运算。
((val=1+1)) # 使用双圆括号时,操作数之间可以加空格
echo $val
let val=1+1 # 使用let命令时,操作数之间不允许出现空格
echo $val
转义字符
touch \$\ \$ # 创建$ $文件
另外,还有一个字符虽然不具有特殊含义,但是要用它做文件名也很麻烦,就是-号。如果要创建一个文件名以-号开
头的文件,这样是不行的: 即使加上\转义也还是报错: 因为各种UNIX命令都把-号开头的命令行参数当作命令的选
项,而不会当作文件名。如果非要处理以-号开头的文件名,可以有两种办法:
1. touch -- -file
2. touch -- ./-file
\还有一种用法,在\后敲回车表示续行,Shell并不会立刻执行命令,而是把光标移到下一行,给出一个续行提示符>,等待
用户继续输入,最后把所有的续行接到一起。
:表示退出码
单引号和双引号的区别
双引号用于保持引号内所有字符的字面值(回车也不例外),但以下情况除外:
$加变量名可以取变量的值
反引号仍表示命令替换
$表示$的字面值
`表示`的字面值(反引号)
\"表示"的字面值
\表示\的字面值 除以上情况之外,在其它字符前面的\无特殊含义,只表示字面值。
条件测试
常见的测试命令包含test或[ ,通过检查该类命令的退出码,决定条件测试是否成立。
eg:
read myint # 标准输入读取myint
test $myint -eq 100 # 这里test后面的参数是命令行参数,需要用空格隔开
[ $myint -eq 100 ] # 注意这里[]之间的空格
echo $? # 打印上个进程的退出码
shell认为退出码为0表示测试条件成立,非0表示测试条件不成立。
整数测试
-eq(等于equal) -ne(不等于not equal) -lt(小于less than)
-gt(大于greater than) -le(小于等于less equal) -ge(大于等于greater equal)
字符串测试
==(相等,注意用空格间隔) !=(不相等) -z(判断空字符串) -n(非空)
[ $mystr=='hehe' ] #这里相当于[]内部的是个整体,不再是多个命令行参数了,因此一直为真。
如果mystr输入的是回车符,那么会报语法错误,就变成了[ == 'hehe' ],实际上,读取变量相当于宏替换。
修改为: [ 'X'$mystr == 'Xhehe' ] 相当于添加一个字符,不管输入是不是回车,都可以。
sh -x second.sh #可以定位到错误,并且把每行执行的命令内容以及替换后的内容显示出来。
[ -z $mystr ]
[ -n $mystr ]
文件测试(实际上也可以判断该文件是否存在)
-d -f -b -c
c字符文件 /dev/stdout -> 最终指向的文件就是字符文件(只能一个一个地读取)
[ -c /dev/pts/3 ]
b块存储文件(磁盘) 可以随机读取
[ -b /dev/sda ]
d目录文件
f普通文件
多条件测试
逻辑非! 注意要加空格,否则就是字符串拼接
[ ! $myint == 10 ]
逻辑与-a (and)
[ $myint1 -eq 1 -a $myint2 -eq 2 ]
逻辑或-o (or)
[ $myint1 -eq 1 -o $myint2 -eq 2 ]
条件分支
if/then/elif/else/fi
read myint
if [ $myint -eq 100 ]
then
echo "success"
fi
如果两条命令写在同一行则需要用;号隔开,比如后面加上then,一行只写一条命令就不需要写;号了。
存在if字眼的地方下面都要加then。
read myint
if [ $myint -eq 100 ]
then
echo "equal 100"
elif [ $myint -lt 100 ]
then
echo "less than 100"
else
echo "greater than 100"
fi
grep
-E使用扩展正则匹配
-q使用安静模式匹配 (不打印查找结果,只反映找没找到)
grep查找结果是有返回值的,匹配到就返回0,反之返回非0。
: 是一个特殊的命令,称为空命令,不做任何事,但Exit Status总是真。此外,
也可以执行/bin/true或/bin/false得到真或假的Exit Status。
|| 和 &&
&&相当于"if...then...",而||相当于"if not...then..."。&&和||用于连接两个命令,而上面讲的-a和-o仅用于在测试表
达式中连接两个测试条件,要注意它们的区别。
make clean && make -j 8 # -j表示开启多线程模式,开辟8个线程 如果清空成功,则执行多线程
case/esac
Shell脚本的case可以匹配字符串,末尾必须以;;结束(类似于break)。
myval="start"
case $myval in
"start" )
echo "start..."
;;
"stop" )
echo "stop..."
;;
* )
echo "default"
;;
esac
$1 是一个特殊变量,在执行脚本时自动取值为第一个命令行参数。$0是可执行文件的名字。
[Ss]tart | "-s" ) #多个选项并且单个选项中某些字符可选
nohup ./http_server 0 9000 & #启动守护进程
server_pid=`pidof http_server` #获取pid
kill -9 $server_pid
循环
for循环
for i in {1..100} {a..z} #如果不加空格打印出来的是两个序列的排列组合
do
touch file.$i
done
---------------------------------------
for ((i=0; i<=10; i++))
do
echo "hello $i" # echo加上-n选项不换行
done
while循环
while循环,一定要注意负责进行索引的自增。
i=0
while [ $i -lt 5 ]
do
echo "$i"
((i++))
#let i++
done
until循环
与while循环判断条件相反
死循环
for ((;;))
do
echo "hha"
done
------------------------
while : # while true 与 until false 都等价于左边的while :
do
echo "hah"
done
不允许出现空语句。
命令行循环
i=0; while [ $i -le 10 ]; do echo "hello bit $i"; let i++; done
计算1-100的和
i=1
sum=0
str=''
for ((i = 1; i <= 100; ++i))
do
if [ -z $str ]
then
str=$i
else
str=$str"+"$i
fi
let sum+=i
done
echo $str"="$sum
1-100的奇数和
i=1
sum=0
str=''
while [ $i -le 100 ]
do
let sum+=i
if [ -z $str]
then
str=$i
else
str=$str"+"$i
fi
let i+=2
done
echo $str"="$sum
位置参数和特殊变量
$0相当于argv[0],也就是命令行参数
$?表示上一条命令的退出状态
$#相当于argc-1
$@表示参数列表$1 $2 ...例如可以用在for循环中的in后面
$$当前shell的进程id
即使越界,也不会崩溃。
shift
位置参数可以用shift命令左移,不带参数的shift命令相当于shift 1,再比如shift 3表示原来的$4变成$1。
$0一直不变,就是可执行文件。
shift 3相当于把左边三个命令行参数挤出去。
echo "\$0 -> $0"
echo "\$1 -> $1"
echo "\$2 -> $2"
echo "\$3 -> $3"
---------------------------------
shift 1
---------------------------------
echo "\$0 -> $0"
echo "\$1 -> $1"
echo "\$2 -> $2"
echo "\$3 -> $3"
命令行输入 ./third.sh a b c
$0 -> ./third.sh
$1 -> a
$2 -> b
$3 -> c
$0 -> ./third.sh
$1 -> b
$2 -> c
$3 ->
遍历命令行参数
for i in $@ # $@表示参数之间用空格隔开
do
echo "$i"
done
----------------------------------
while [ $# -ne 0 ]
do
echo $1
shift 1
done
函数
函数定义中没有返回值也没有参数列表。函数就相当于一个命令,也可以看做是一个迷你版的脚本。
Shell脚本中的函数必须先定义后调用,一般把函数定义都写在脚本的前面,把函数调用和其它命令写在脚本的最后(类
似C语言中的main函数,这才是整个脚本实际开始执行命令的地方)。
shell函数没有参数列表并不表示不能传参数,事实上,函数就像是迷你脚本,调用函数时可以传任意个参数,在函数内同样
是用$1、$2等变量来提取参数。
传参相当于命令行传参,不用加括号,没有形参的概念,函数名相当于可执行程序。
缺点:
试验表明,$0 并不会作为函数参数,从事传参任务。
函数中的位置参数相当于函数的局部变量,改变这些变量并不会影响函数外面的$1 、$2 等变量。
函数调用或者返回时,将shell函数当成命令。只要是命令,那么函数调用成功与否,可以通过$?来判定。一般函
数中可以用return命令返回,如果return后面跟一个数字则表示函数的ExitStatus。
function myfun()
{
return 11
}
echo $?
eg:
function myfun()
{
echo "hehe"
echo "$1"
echo "$2"
echo "$3"
}
myfun aa bb cc
注意函数体的左花括号{和后面的命令之间必须有空格或换行,如果将最后一条命令和右花括号}写在同一行,命令末尾必须有;号。
如果没有参数的话只需要函数名就可以调用函数了,有参数的话就传入位置参数。
function myfunc()
{
echo "hehe"
}
val=`myfunc` # ret=$(myfunc)
echo $val
函数返回字符串 (使用``来接收)
shell脚本的调试
-n检查脚本的语法错误
-v一边执行脚本,一边将执行的脚本命令打印到标准错误输出
-x提供跟踪执行信息,将执行的每一条指令和结果依次打印出来
set -x和set +x分别表示开启和禁用-x参数,也可以只对脚本的某一段进行跟踪调试(-x +x)。 使用bash -x third.sh
当然也可以在脚本开始she-bang处加上-x
数组
Bash Shell只支持一维数组(不支持多维数组),初始化时不需要定义数组大小(与 PHP类似),并且没有限定数组的大小。
与大部分编程语言类似,数组元素的下标由0开始。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0。
Shell数组用括号来表示,元素用"空格"符号分割开,可以不使用连续的下标,而且下标的范围没有限制。
array_name=(value1 value2 value3 ... valuen)
读取数组指定元素
${array_name[index]}
eg:
array=(1 10 3.14 'a')
echo ${array[0]}
echo ${array[1]}
echo ${array[2]}
echo ${array[3]}
echo ${#array[@]} #数组长度 数组元素可以不连续
echo ${array[@]} #数组元素列表
shell与文件
输出重定向 > (覆盖式的重定向)
追加重定向 >>
for ((i=1; i<=10; ++i))
do
echo $i >> test.txt
done
-------------------------
for ((i=1; i<=10; ++i))
do
echo $i
done > test.txt # 相当于只输出一次
-------------------------
输入重定向 <
while read line # 循环读取一行
do
echo $line
done < test.txt
字符串变换
while read line
do
echo "hello"$line
done < test.txt > test2.txt # 先输入重定向后输出重定向
find / -name vim 2>test.txt # 将出错信息重定向到test.txt,其中2>是粘在一起的
find / -name vim 2>>test.txt # 追加输出错误信息
make的出错信息也可以重定向
Here Document
用来将输入重定向到一个交互式 Shell 脚本或程序,它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给command
语法:
command << delimiter
document
delimiter
结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。
开始的delimiter前后的空格会被忽略。