Linux信号
信号 | 值 | 描述 |
---|---|---|
1 | SIGHUP | 挂起进程 |
2 | SIGINT | 终止进程 |
3 | SIGQUIT | 停止进程 |
9 | SIGKILL | 无条件终止进程 |
15 | SIGTERM | 尽可能终止进程 |
17 | SIGSTOP | 无条件停止进程 |
18 | SIGTSTP | 停止或暂停进程 |
19 | SIGCONT | 继续运行停止的进程 |
(1)中断进程:Ctrl+C组合发送SIGINT信号
(2)暂停进程:Ctrl+Z组合发送SIGTSTP
方括号中的数字是shell分配的作业号。
查看已停止的作业ps
在S列中,已停止的作业状态显示为T(STOP)
捕获信号
trap commands signals
signals用空格分开,可以用数值或Linux信号名来指定。
使用trap来忽略SIGINT
trap "echo 'Sorry! I have trapped Ctrl-C' " SIGINT
命令会在每次检测到SIGINT信号时显示一行简单的文本信息,捕获这些信号阻止用户用组合键停止程序
捕获脚本退出
在脚本退出时进行捕获
trap "echo Goodbye..." EXIT
修改或移除捕获
在脚本中不同位置进行不同的捕获处理,只需重新使用带有新选项的trap命令。
trap "echo 'Sorry...Ctrl-C is trapped.'" SIGINT
count=1
while [ $count -le 5 ]
do
echo "Loop #$count"
sleep 1
count=$[ $count +1 ]
done
trap "echo I modified the trap" SIGINT
count=1
while [ $count -le 5 ]
do
echo "Second Loop #$count"
sleep 1
count=$[ $count +1 ]
done
删除已设置好的捕获trap --SIGINT
后台运行脚本
只要在命令后加个&就可以了。
./test4.sh &
方括号内的是进程的作业号,3231是进程ID(PID)。PID是唯一的。
当后台进程结束时,会显示:
运行多个后台作业
./test6.sh &
./test7.sh &
./test8.sh &
.....
.....
查看所有脚本的状态:ps
在非控制台下运行脚本
在终端会话中启动shell脚本,然后让脚本一直以后台模式运行到结束,即使你退出了终端会话
nohup ./test.sh &
使用nohup命令,即使关闭该会话脚本也会忽略终端发过来的SIGHUP信号。
nohup命令会自动把STDOUT和STDERR的消息重定向到nohup.out文件中。
查看作业
jobs命令允许查看shell当前正在处理的作业
jobs
jobs [-lnprs]
option | mean |
---|---|
-l | 列出进程的PID以及作业号 |
-n | 只列出上次shell发出的通知后改变状态的作业 |
-p | 只列出作业的PID |
-r | 只列出运行中的作业 |
-s | 只列出已停止的作业 |
脚本用$$变量来显示Linux分配给该脚本的PID
kill命令
向默认进程发送一个SIGHUP信号,终止该作业
kill PID
重启停止的作业
以后台模式重启作业 bg 作业号
以前台模式重启作业 fg 作业号
nice命令
nice命令允许你设置命令启动时的调度优先级。(-20 ~ +19)
nice -n 优先级别 command
用at命令来计划执行作业
at [-f filename] time
-f指定脚本文件
时间格式 | |
---|---|
标准的小时和分钟格式 | 10:15 |
AM/PM指示符 | 10:15PM |
特定的可命名时间 | midnight、now、moon、teatime |
标准的日期格式 | MMDDYY、MM/DD/YY |
文本日期 | Jul 4 |
时间增量 | 10:15+7天 |
at -f test13.sh now
at -M -f test13.sh now #-M选项用来屏蔽作业产生的输出信息
列出等待的作业 atq
删除作业 atrm 作业号
cron时间表
格式:
min hour dayofmonth month dayofweek command
cron时间表允许你用特定值、取值范围或者通配符指定条目。
想在每天10:15运行一个命令
15 10 * * * command
指定每周一4:15PM运行的命令
15 16 * * 1 command
可用三字符的文本值(mon、tue、wed、thu、fri、sat、sun)或数值(0为周日,6为周六)来指定dayofweek
每个月第一天中午12点执行命令
00 12 1 * * command
dayofmonth指定月份中的日期值(1~31)
构建cron时间表
crontab -l
列出已有的时间表
浏览cron目录
ls /etc/cron.*ly
创建函数
格式一:
function name {
commands
}
格式二:
name() {
commands
}
使用函数
在行中指定函数名即可。函数必须在使用前被定义
function func1 {
echo "This is an example of a script"
}
count=1
while [ $count -le 5 ]
do
func1
count=$[ $count +1 ]
done
echo "This is the end of a loop"
func1
echo "Now this is the end of the script"
默认退出状态码
可以用$?来生成退出状态码,但退出码是函数中最后一条命令返回的退出码
#退出码非0
echo "trying to display a non-existent file"
ls -l badfile
#退出码为0
ls -l badfile
echo "This was a test of bad command"
如何解决这个问题?
使用return命令
bash shell使用return来退出函数并返回特定的退出状态码。
function db1 {
read -p "Enter a value:" value
echo "doubing the value"
return $[ $value * 2 ] #指定$?值
}
db1
echo "The new value is $?"
但是退出码必须是0-255范围内的,如果超出该范围返回除以256的余数
使用函数输出
将函数的值保存到变量中 result=$( function)
函数会用echo显示计算结果
function db1 {
read -p "Enter a value:" value
echo $[ $value * 2 ]
}
result=$(db1)
echo "The new value is $result"
向函数传递参数
函数可以使用标准的参数环境变量来表示命令行上传到函数的参数。
函数名会在$0中被定义,函数的参数通过$1、$2等来定义;可以用 $#来判断传到函数的参数数目。
#定义函数
function addem {
if [ $# -eq 0 ] || [ $# -gt 2 ]
then
echo -1
elif [ $# -ne 1 ]
then
echo $[ $1 + $1 ]
else
echo $[ $1 + $2 ]
fi
}
#调用函数
val1=$(addem 10 15)
val2=$(addem 10)
val3=$(addem 10 15 20)
在函数中处理变量
全局变量
在shell脚本中任何地方都有效的变量。在函数外定义的变量可以在函数内正常访问
function db1 {
value=$[ $value * 2 ]
}
read -p "Enter a value:" value #450
db1
echo "The new value is $value" #900
$value 变量在函数外定义并被赋值。当 dbl 函数
被调用时,该变量及其值在函数中都依然有效。
如果变量在函数内被赋予了新值,那么在脚本中
引用该变量时,新值也依然有效。
局部变量
无需在函数中 使用全局变量,将函数内的变量声明为局部变量
local 变量名
local temp=$[ $value + 5 ]
local保证变量只局限在该函数中。如果函数外有同样名字的变量,shell会保持这两个变量的值是分离的。
function func1 {
local temp=$[ $value + 5 ]
result=$[ $temp * 2 ]
}
temp=4
value=6
func1
echo "The result is $result" #22
数组变量和函数
向函数传数组参数
将数组变量当做单个参数传递的话,会不起作用。
从函数返回数组
函数递归
函数可以递归地调用,函数可以调用自己来得到结果。通常递归函数都有一个最终可以迭代到的基准值。
function factorial {
if [ $1 -eq 1 ]
then
echo 1
else
local temp=$[ $1 -1]
local result=`factorial temp`
echo $[ $result * $1 ]
fi
}
创建库
step1:创建一个包含脚本所需函数的公用库文件。这里有个叫作myfuncs的库文件,它定义3个简单的函数。
function addem {
echo $[ $1 + $2 ]
}
function multen {
echo $[ $1 * $2 ]
}
function divem {
if [ $2 -eq 0 ]
then
echo -1
else
echo $[ $1 / $2 ]
fi
}
step2:source命令会在当前shell上下文执行命令,而不是创建一个新shell。可以用source来运行库文件脚本。
source的快捷别名是 点操作符…
../myfuncs
在命令行上创建函数
(1)单行定义
function divem echo $[ $1 / $2 ];
在命令行上定义函数时,必须在每个命令后面加个分号。
(2)多行定义函数,不用再每条命令末尾加分号;但要使用花括号。
在.bashrc文件中定义函数
在.bashrc文件定义函数,每次启动新shell都会中心载入。
(1)直接定义函数
把写的函数直接定义在文件末尾即可
(2)读取函数文件
sed编辑器
特点:
(1)一次从输入中读取一行数据
(2)根据所给的命令匹配数据
(3)按照命令修改流中的数据
(4)将新的数据输出到STDOUT
sed options script file
option | description |
---|---|
-e script | 将script中指定的命令添加到已有的命令中 |
-f file | 将file中的指定的命令 添加到已有的命令中 |
-n | 不产生命令输出,使用print命令完成输出 |
在命令行定义编辑器命令
echo "This is a test" | sed 's/test/big test/'
sed中的s命令会用斜线间指定的第二个文本字符串来替换第一个文本字符串模式。test被替换成big test。
sed 's/dog/cat' data1.txt
这个命令将数据中的dog都替换成cat;再将修改后的数据发送到STDOUT,实际文件不会被修改。
sed -e 's/brown/green/;s/dog/cat/' data1.txt
sed -e可以在命令行上执行多个命令,命令之间用分号隔开;命令末尾和分号之间不能有空格。
sed -e '
s/brown/green/
s/fox/elephant/
s/dog/cat/' data1.txt
当你不想用分号,可以用bash shell中的次提示符来分割命令。
从文件中读取编辑器命令
sed -f 指定脚本文件 处理数据
sed -f script1.sed data1.txt
为了避免将sed编辑器脚本文件和bash shell文件搞混,使用.sed作为sed脚本文件的扩展名
gawk(awk)
gawk options program file
options | description |
---|---|
-F fs | 指定划分数据字段的分割符 |
-f file | 指定的文件中读取程序 |
-v var=value | 定义一个变量 |
-mf N | (max field)0指定要处理数据文件中的最大字段数 |
-mr N | (max row)指定数据文件中的最大数据行数 |
-W keyword | 指定gawk警告等级 |
从命令行中读取程序脚本
必须将脚本命令放到两个花括号({})中,还要用单引号把命令和花括号圈起来'{commands}'
gawk '{print "Hello World!"}'
使用数据字段变量
$0 | 整个文本行 |
---|---|
$1 | 文本中的第一个数据字段 |
$2 | 文本行中的第二个数据字段 |
$n | 文本行中第n个数据字段 |
在文本行中,每个数据字段都是通过字段分割符划分的。默认的字段分割符是任意空白字符
gawk '{print $1}' data2.txt
采取其他字段:-F
显示系统密码文件的第一个数据字段
gawk -F: '{ print $1}' /etc/passwd
在程序脚本中使用多个命令
在命令行上的程序脚本中使用多条命令,只要在命令之间放个分号即可。
先给字段变量$4赋值,在打印出整个数据字段
echo "My name is Rich" | gawk '{$4="keiji";print $0}'
gawk '{
$4="keiji"
print $0}'
从文件中读取程序
gawk -F: -f script1.gawk /etc/passwd
在程序文件中指定多条命令,只要一条命令放一行即可,不需要用分号
在处理数据前运行脚本:BEGIN
强制gawk在读取数据前执行BEGIN关键字指定的程序脚本
gawk 'BEGIN{print "Hello world!"}'
如果想使用正常的程序脚本处理数据,必须要用另一个脚本区域来定义程序两段脚本仍然被认为是 gawk 命令行中的一个文本字符串。你
需要相应地加上单引号
gawk 'BEGIN{print "The data3 File contents:"}
{print $0}' data3.txt
在处理数据后运行脚本:END
gawk会在打印完文件内容后,执行END脚本中的命令。
gawk 'BEGIN{print "The data3 file Contents:"} {print $0} END{print "End of file"}' data3.txt
gawk 'BEGIN{
print "The latest list of users and shells"
print "UserID \t Shell"
print "-------- \t -------"
FS=":"
}
{
print $1 "\t" $7
}
END{
print "This concludes the listing"
}' /etc/passwd
sed:更多的替换选项
(1)替换标记
替换命令默认情况下只替换 每行中出现的第一处。要让替换命令替换一行中出现的所有地方,必须要使用替换标记(substitution flag)。
s/pattern/replacement/flags
数字 | 表明新文本较替换第几处模式匹配的地方 |
---|---|
g | 替换所有匹配的文本 |
p | 表明原先行的内容要打印出来 |
w file | 将替换的结果写到文件中 |
只替换每行中第二次出现的匹配模式
sed 's/test/trial/2' data.txt
替换文本中所有匹配的地方
sed 's/test/trial/g data.txt'
p替换标记通常和-n选项一起使用,二者配合使用的效果就是只输出被替换命令修改过的行
sed -n 's/test/trial/p data.txt'
w替换标记产生同样的输出,不过输出保存到指定文件。
sed 's/test/trial/w test5.txt' data5.txt
(2)替换字符
因为脚本用正斜线(/)当做字符串的分割符号,所以当替换内容含有/时,要在他前面加上反斜线(\)来转义。
替换/etc/passwd 文件中的bash shel
sed 's/\/bin\/bash/\/bin\/csh/ /etc/passwd'
这种写法很难看而且容易出错,sed允许用其他符号在作为替换命令的字符串分割符
sed 's!/bin/bash!/bin/csh!' /etc/passwd
使用地址
如果只想将命令作用于特定的行或某些行,必须用行寻址
(1)以数字形式表示行区间
sed会将文本流中的第一行编号为1。命令行指定的地址可以是 单行号
(一行) 或者 起始行号,结尾行号
(一个范围)
只修改第二行的文本
sed '2s/dog/cat/ data.txt'
修改2~3行的文本
sed '2,3s/dog/cat/ data.txt'
修改第二行开始到结尾的文本,用美元符表示结尾行
sed '2,$s/dog/cat/ data.txt'
(2)用文本模式来过滤行
格式/pattern/command
必须用正斜线将要指定的pattern封起来
只想修改用户Samantha的默认shell
sed '/Samantha/s/bash/csh' /etc/passwd
(3)命令组合
#格式
address{
command1
command2
command3
}
#例子1
sed '2{
s/fox/elephant/
s/dog/cat/
}' data.txt
#例子2
sed '2,${
s/brown/green/
s/dog/cat/
}' data.txt
删除行
删除行命令d
删除所有文本行
sed 'd' data.txt
删除第三行
sed '3d' data.txt
删除第2~3行
sed '2,3d' data.txt
删除第3行到最后一行
sed '3,$d' data.txt
删除含有number 1的行
sed '/number 1/d' data.txt
可以使用两个文本模式来删除某个区间的行,指定的第一个模式会“打开”删除功能;第二个模式会“关闭”行删除功能。sed会删除两个指定行之间的所有行。
sed '/1/,/3/d' data.txt
插入和附加文本
插入(insert)命令(i)会在指定行前增加一个新行
附加(append)命令(a)会在指定行后增加一个新行
格式
sed '[address]command\new line'
例子:
echo "Test Line 2" | sed 'i\Test Line 1'
echo "Test Line 2" | sed 'a\Test Line 1'
将一个新行插入到数据流第三行前
sed '3i\This is an inserted line' data.txt
将一个新行附加到数据流的第三行后
sed '3a\This is an appended line.' data.txt
将一个新行添加到数据的最后一行
sed '$a\This is a new line of txt' data.txt
如果要插入或附加多行文本,必须在新文本中的每一行后使用反斜线,直到最后一行
sed '1i\
This is one line of new text.\
This is another line of new text.' data.txt
修改行
修改(change)命令©允许修改数据流整行的内容。
格式
sed '[address]command\new line'
修改第三行中的文本
sed '3c\This is a changed line of text' data.txt
修改含有“number 1”文本的内容
sed '/number 1/c\This is a changed line of text' data.txt
sed会用一行文本来替换数据流中的两行文本,而不是逐一修改这两行的文本
sed '2,3c\This is a new line of text '
转换命令
转换(transform)命令(y)的格式:
[address]y/inchars/outchars
转换命令对inchars和outchars值进行一对一对应:inchars中的第一个字符会被转换为outchars中的第一个字符,第二个字符会被转换成outchars中的第二个字符
sed 'y/123/789/' data.txt
转换命令是一个全局命令,你无法限定只转换特定地方出现的字符。
echo "This 1 is a test of 1 try | sed 'y/123/456/'"
回顾打印
p命令用来打印文本行
等号(=)命令用来打印行号
l命令列出行
(1)打印行
p所做的就是打印已有的数据文本。
echo "this is a test"| sed 'p'
p与-n连用只打印包含匹配文本模式的行
sed -n '/number 3/p' data.txt
快速打印数据流中的某些行
sed -n '2,3p' data.txt
如果需要在修改之前查看行:
sed -n '/3/{
p
s/line/test/p
}' data.txt
(2)打印行号
sed '=' data.txt
sed -n '/number 4/{
=
p
}' data.txt
利用 -n 选项,你就能让sed编辑器只显示包含匹配文本模式的行的行号和文本。
(3)列出行
列出( list )命令( l )可以打印数据流中的文本和不可打印的ASCII字符。
cat data.txt
sed -n 'l' data.txt
制表符的位置用\t来显示,行尾的美元符表示换行符
使用sed处理文件
1.写入文件
w命令用来向文件写入行,filename可以使用相对路径或绝对路径。
格式:
[address]w filename
将前两行打印到一个文本文件中
sed '1,2w test.txt' data.txt
如果你不想让行显示到STDOUT上,可以用sed -n
sed -n '/Browncoat/w Browncoat.txt' data.txt
2.从文件读取数据
读取(read)命令(r)可以将一个独立文件中的数据插入到数据流中
格式:
[address]r filename
在第三行后插入一个文本
sed '3r data.txt' source.txt
在含有number 2 的行后插入一个文本
sed '/number 2/r data.txt' source.txt
要在数据流的末尾添加文本
sed '$r data.txt' source.txt
正则表达式
纯文本
正则表达式区分大小写,只会匹配大小写也相符的模式
可以在正则表达式中使用空格和数字
在正则表达式中,不用写完整的数字,只要定义的文本出现在数据流中正则表达式就能匹配
echo "This is a test" | sed -n '/test/p'
echo "This is a test" | sed -n '/trial/p'
echo "This is a test" | gawk '/test/{print $0}'
echo "This is a test" | gawk '/trial/{print $0]'
正则表达式识别的特殊字符:. [] ^$ +?| ()*
如果要把特殊符号当做普通文本字符,在前面加反斜线(\)就可以了
查找文本中的美元符
sed -n '/\$/p' data.txt
使用反斜线也需要转义
sed -n '/\\/p'
使用正斜线,需要转义
echo "3 / 2" | sed -n '/\//p'
锚字符
锚字符用来将模式锁定在数据库的行首和行尾。
(1)锁定在行首 ^
echo "The book store" | sed-n '/^book/p'
匹配不成功
echo "Books are expensive" | sed -n '/^Book/p'
匹配成功
如果你将^放在模式开头之外的其他位置,那么它就跟普通字符一样,不在是特殊字符了。
echo "This ^ is a test" | sed -n '/s ^/p'
如果指定正则表达式模式时只用了脱字符,就不需要用反斜线来转义。但如果你在模式中先指定了脱字符,随后还有其他一
些文本,那么你必须在脱字符前用转义字符。
(2)锁定在行尾 $
#success
echo "This is a good book" | sed -n '/book$/p'
#fail
echo "This book is good" | sed -n '/book$/p'
(3)组合锚点
将行首锚点和行尾锚点组合使用:
查找只含有特定文本模式的数据行
sed -n '/^This is a test$/' data.txt
两个锚点直接组合在一起,这样过滤出数据流中的空白行
sed '/^$/d' data.txt
(4)点号字符
点号用来匹配除换行符之外的任意单个字符,它必须匹配一个字符。空格也是字符
sed -n '/.at/p' data.txt
字符组
字符组可以定义用来匹配文本模式中某一位置的一组字符。用方括号定义数组,里面包含所有你希望出现在该字符组的字符。
匹配"cat"或者“hat”
sed -n '/[ch]at/p data.txt'
在不太确定某个字符的大小写时
echo "Yes" | sed -n '/[Yy]es/p'
echo "yes" | sed -n '/[Yy]es/p'
可以在单个表达式中用多个字符组,正则表达式使用3个字符组来涵盖3个字符位置含有大小写的情况
echo "Yes" | sed -n '/[Yy][Ee][Ss]/p'
用正则表达式匹配含有数字0、1、2或3的行
sed -n '/[0123]/p' data
可以将字符组组合在一起,检查数字是否具备正确的格式,比如电话号码和邮政
sed -n '
/[0123456789][0123456789][0123456789][0123456789][0123456789]
[0123456789]/p' data
#这个脚本也能通过6位数,即使我们只定义了5个字符组
#解决方案:
sed -n '
/^[0123456789][0123456789][0123456789][0123456789][0123456789]
[0123456789]$/p' data
排除型字符组
排除型字符组可以寻找组中没有出现的字符,而不是去寻找组中含有的字符。在字符组开头加个脱字符。
sed -n '/[^ch]at/p' data.txt
注意:以at开头的行仍未能匹配模式,因为字符组不许匹配一个字符
区间
可以用单破折线符号在字符组中表示字符区间,只需要指定区间的第一个字符、单破折号以及区间的最后一个字符就行。
sed -n '/^[0-9][0-9][0-9][0-9][0-9]$/p' data
同样的方法也适用于字母,匹配首字母在c和h之间的单词
sed -n '/[c-h]at/p' data.txt
可以在单个字符组指定多个不连续的区间。该字符组允许区间ac、hm中的字母出现在 at文本前,但不允许出现d~g的字母
sed -n '/[a-ch-m]at/p' data.txt
特殊字符组
group | description |
---|---|
[ [:alpha:] ] | 匹配任意字母符号,不管大小写 |
[ [:alnum:] ] | 匹配任意字母数字字符(0-9,A-Z,a-z) |
[ [:space:] ] | 匹配任意空白字符 |
[ [:digit:] ] | 匹配0-9之间的数字 |
[ [:lower:] ] | 匹配小写字符字符a-z |
[ [:upper:] ] | 匹配大写字母字符A-Z |
[ [:punct:] ] | 匹配标点字符 |
[ [:print:] ] | 匹配任意可打印字符 |
[ [:blank:] ] | 匹配空格或制表符 |
echo "abc" | sed -n '/[ [:digit:] ]/p'
echo "abc" | sed -n '/[ [:alpha:] ]/p'
echo "abc123" | sed -n '/[ [:digit:] ]/p'
echo "This is ,a test" | sed -n '/[ [:punct:] ]/p'
星号
在字符后面放置星号表明该字符必须在匹配模式的文本中出现0或多次
如果字段出现ik、iek、ieek、ie…k都会匹配成功
sed -n '/ie*k/p'
解决美式和英式单词的拼写问题(color colour)
sed -n '/colou*r/p'
把星号和点号组合起来,能够匹配任意数量的任何字符
.*
星号用在字符组上,允许指定可能在文本中出现多次的字符组或字符区间
如果字段出现bt、bat、bet、baat、baaeet都可以匹配上。只要a和e字符以任何组合形式出现在 b 和 t 字符之间
sed -n '/b[ae]*t/p'
拓展正则表达式
gawk程序可以使用大多数扩展正则表达式模式符号,并且能提供一些额外过滤功能,而这些功能都是
sed编辑器所不具备的。
问号
问号表示前面的字符可以出现0次或1次
文件出现bt或bet才能匹配上
gawk '/be?t/{print $0}'
可以将问号与字符组一起使用,文件出现bt、bat或bet才能匹配上。
gawk '/b[ae]?t/{print $0}'
加号
加号表明前面的字符可以出现1次或多次。必须至少出现1次。
文本出现bet、beet、be…t才会被匹配上
gawk '/be+t/{print $0}'
可以将加号与字符组组合使用
gawk '/b[ae]+t/{print $0}'
使用花括号
{m} | 正则表达式准确出现m次 |
---|---|
{m,n} | 正则表达式至少出现m次,至多n次 |
gawk程序不会识别正则表达式间隔。必须指定gawk程序的 --re-interval 命令行选项才能识别正则表达式间隔
gawk --re-interval '/be{1,2}t/{print $0}' #匹配 bet或beet
在字符组中同样适用
gawk --re-interval '/b[ae]{1,2}t/{print $0}'
管道符号
格式:
expr1|expr2|...
管道符号在正则表达式中表示“或”
匹配文本中的cat或dog
gawk '/cat|dog/{print $0}'
匹配文本中的cat、hat或dog
gawk '/[ch]at|dog/{print $0}'
表达式分组
正则表达式中可以用圆括号分组。该组会被视为一个标准符号。
匹配Sat或Saturday
gawk '/Sat(urday)?/{print $0}'
匹配cab、cat、bab、bat
gawk '/(c|b)a(b|t)/{print $0}'