sed:Stream EDitor,流编辑器,行编辑器:实现文本编辑时是逐行进行的。
用法:
sed [option]... 'script' inputfile...
script:‘地址命令’,如'1,4d'
sed是将文件内容读取后放入自己的编辑缓存中,这个缓存空间叫做sed的模式空间--pattern space,对缓存中的副本进行的编辑,最终要将模式空间中的内容输出到屏幕(输出流)。
常用选项:
-n:不输出模式空间中的内容至屏幕。
-e:多点编辑。
-f /PATH/TO/SCRIPT_FILE:从指定文件中读取编辑脚本;
-r:支持使用扩展的正则表达式;
-i:原处编辑,修改源文件
地址定界:
(1)不给地址:对全文进行处理;
(2)单地址:#——指定的行;/pattern/——被此处模式所能匹配的每一行
(3)地址范围:#,#; #,+#; /pat1/,/pat2/; #,/pat1/
(4)~:步进:1~2 ——显示奇数行; 2~2 ——显示偶数行
编辑命令:
d:删除
p:显示模式空间中的内容
a \text:在行后面追加文本,支持使用\n实现多行追加
i \text:在行前面插入文本,支持使用\n实现多行插入
c \text:替换行为单行或多行文本;
w /path/to/somefile:保存模式空间中匹配的行内容至指定文件中;
r /path/from/somefile:读取指定文件的文本流至模式空间中匹配的行后;
=:为模式空间中的行打印行号;
!:取反条件;
s///:支持使用其他分隔符,s@@@,s###,查找替换
替换标记:g——行内全局替换;p——显示替换成功的行; w /path/to/somefile——保存
sed除了有一个模式空间,还有一个保持空间(hold space)
高级编辑命令:
h:把模式空间中的内容覆盖至保持空间中;
H:把模式空间中的内容追加至保持空间中;
g:从保持空间中取出数据覆盖至模式空间;
G:从保持空间中取出数据追加至模式空间;
x:把模式空间中内容与保持空间中的内容进行互换;
n:读取匹配到的行的下一行至模式空间;
N:追加匹配到的行的下一行至模式空间;
d:删除模式空间中的行;
D:删除多行模式空间中的所有行;
sed -n 'n;p' file :显示偶数行
sed '1!G;h;$!d' file:逆向显示文件内容
sed '$!N;$!D' file:取文件最后两行
sed '$!d' file:取文件最后一行
sed ‘G’:每一行后加一空白行
sed ‘/^$/d;G’ file:多个空白行合并成一个,每行后加一空白行
sed ‘n;d’ file:显示奇数行
sed -n '1!G;h;$p' file:逆向显示文件中的每一行
Bash脚本编程续:
until CONDITION;do
循环体
done
until与while类似,不同之处是进入条件是CONDITION为false,退出条件是CONDITION为true,正好与while相反。
计算100以内正整数的和:
#!/bin/bash
#
declare -i i=1
declare -i sum=0
until [ $i -gt 100 ]; do
let sum+=$i
let i++
done
echo "Sum:$sum"
循环控制语句(用于循环体中):
continue [N]:让某次循环提前结束,带N提前结束第N层的本次循环,而直接进入下一轮循环
while CONDITION;do
CMD1
...
if CONDITION2; then
continue
fi
CMDn
...
done
break [N]:提前结束循环,或提前结束第N层循环
while CONDITION;do
CMD1
...
if CONDITION2; then
break
fi
CMDn
...
done
示例:每隔3秒到系统上获取登录的用户信息,如果user1登录了,则记录于日志中,并退出。
#!/bin/bash
#
read -p "Enter a user name:" username
while true; do
if who | grep "^$username" &> /dev/null; then
break
fi
sleep 3
done
echo "$username logged on." >> /tmp/user.log
#!/bin/bash
#
read -p "Enter a user name:" username
until who | grep "^$username" &> /dev/null; do
sleep 3
done
echo "$username logged on." >> /tmp/user.log
while循环的特殊用法(遍历文件的每一行):
while read line;do
循环体
done < /path/from/somefile
依次读取/path/from/somefile文件中的每一行,且将行赋值给变量line;
示例找出ID号为偶数的所有用户,显示其用户名及ID。
#!/bin/bash
#
while read line; do
if [ $[`echo $line | cut -d: -f3` % 2] -eq 0 ]; then
echo -e -n "UserName: `echo $line | cut -d: -f1`\t"
echo "UID: `echo $line | cut -d: -f3 `"
fi
done < /etc/passwd
for循环的特殊格式:
for((控制变量初始化;条件判断表达式;控制变量的修正表达式));do
循环体
done
控制变量初始化:仅在运行到循环代码段时执行一次;
控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断;
示例:显示一个菜单,提示用户输入选项,显示选项内容,菜单如下:
cpu)cpu info
mem)memory info
disk)disk info
quit)quit
#!/bin/bash
#
while true; do
cat << EOF
cpu)cpu info
mem)memory info
disk)disk info
quit)quit
=================
EOF
read -p "Enter a option: " option
while [ "$option" != 'c' -a "$option" != 'm' -a "$option" != 'd' -a "$option" != 'q' ]; do
read -p "Wrong option,Enter again: " option
done
if [ "$option" == 'c' ]; then
lscpu
elif [ "$option" == 'm' ]; then
cat /proc/meminfo
elif [ "$option" == 'd' ]; then
fdisk -l
else
echo "Quit"
exit 0
fi
done
case语句:条件判断
case 变量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
。。。。
*)
默认分支
;;
用case语句实现上例:
#!/bin/bash
#
while true; do
cat << EOF
cpu)cpu info
mem)memory info
disk)disk info
quit)quit
=================
EOF
read -p "Enter a option: " option
while [ "$option" != 'c' -a "$option" != 'm' -a "$option" != 'd' -a "$option" != 'q' ]; do
read -p "Wrong option,Enter again: " option
done
case "$option" in
c)
lscpu
;;
m)
cat /proc/meminfo
;;
d)
fdisk -l
;;
q)
echo "Quit"
exit 0
;;
esac
done
case支持glob风格的通配符:
*:任意长度字符;
?:任意单个字符;
[]:指定范围内的任意单个字符;
a|b:a或b
function:函数
过程式编程:代码重用
模块化编程
结构化编程
定义语法一:
function f_name {
...函数体...
}
定义语法二:
f_name(){
...函数体...
}
调用:函数只有被调用才会执行;
调用:给定函数名
函数名出现的地方,会被自动替换为函数代码;
函数的生命周期:被调用时创建,返回时终止;
使用return命令返回自定义状态结果;0表示成功,1~255表示失败;
如果没有使用return语句,则返回的状态结果是最后一条语句的执行状态结果。
#!/bin/bash
#
username='myuser'
function myadduser {
if id $username &> /dev/null; then
echo "$username exists."
else
useradd $username
[ $? -eq 0 ] && echo "Add $username finished."
fi
}
myadduser
echo $?
以上脚本,不管是否添加用户成功,输出的$?都是0,因为没有return语句,返回的是最后语句的执行状态结果,用户存在,是echo "$username exists."的执行状态结果,用户不存在,是echo "Add $username finished."的执行状态结果。
示例:写一个脚本,完成如下功能:
1)可接受参数:start,stop,restart,status;
2)如果参数非上述四种之一,提示使用格式后报错;
3)如果是start,则创建/var/lock/subsys/SCRIPT_NAME,并显示“启动成功”;
注意:如果事先已经启动过,如何处理?
4)如果是stop,则删除/var/lock/subsys/SCRIPT_NAME,并显示“停止成功”;
注意:如果事先已经停止过或未启动,如何处理?
5)如果是restart,则先stop,再start;
注意:如果本来没有start,如何处理?
6)如果是status,则判断/var/lock/subsys/SCRIPT_NAME存在性,存在显示“SCRIPT_NAME is running....”,不存在显示“SCRIPT_NAME is stopped”
SCRIPT_NAME为当前脚本名。
#!/bin/bash
#
# chkconfig: - 88 12 # 定义服务的运行级别,-表示不定义级别,即所有级别都关闭
# description: add this script to a service
# 上面两行用于将脚本变成服务脚本,即chkconfig管理
prog=$(basename $0)
lockfile=/var/lock/subsys/$prog
start() {
if [ -e $lockfile ]; then
echo "$prog is running."
return 0
else
touch $lockfile
[ $? -eq 0 ] && echo "Starting $prog finished."
fi
}
stop() {
if [ -e $lockfile ]; then
rm -f $lockfile && echo "Stop $prog ok."
else
echo "$prog is stopped yet."
fi
}
status() {
if [ -e $lockfile ]; then
echo "$prog is running."
else
echo "$prog is stopped."
fi
}
myusage() {
echo "Usage:$prog {start | stop | restart | status}"
}
if [ $# -lt 1 ]; then
myusage
exit 1
fi
case $1 in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
status)
status
;;
*)
myusage
;;
esac
函数返回值:
函数的执行结果返回值;
1)使用echo或print命令进行输出
2)函数体中调用命令的执行结果
函数的退出状态码
1)默认取决于函数体中执行的最后一条命令的退出状态码;
2)自定义退出状态码;return
函数可以接受参数:
传递参数给函数:调用函数时,在函数名后以空白分隔给定参数列表即可;如:func arg1 arg2 ..."
在函数体当中,可使用$1,$2...调用这些参数;还可以使用$@,$*,$#等特殊变量;
#!/bin/bash
#
function myadduser {
if [ $# -lt 1 ]; then
echo "missing arguments."
return 2
fi
if id $1 &> /dev/null; then
echo "$1 exists."
return 1
else
useradd $1
[ $? -eq 0 ] && echo "Add $1 finished."
return 0
fi
}
myadduser userpp
echo $?
变量作用域:
本地变量:当前shell进程:为了执行脚本会启动专用的shell进程;因此,本地变量的作用范围是当前shell脚本程序文件;
局部变量:函数的生命周期:函数结束时变量被自动销毁;函数中局部变量需要使用local来定义;
如果函数中有局部变量,其名称同本地变量;
函数递归:函数直接或间接调用自身
#!/bin/bash
#
fact() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
echo 1
else
echo $[$1*$(fact $[$1-1])]
fi
}
fact 6
数组:存储多个元素的连续的内存空间
变量:存储单个元素的内存空间;
数组引用:
数组名
索引:编号从0开始,属于数值索引
注意:索引也可以支持使用自定义的格式,而不仅仅是数值格式;
bashde数组支持稀疏格式;
引用数组中的元素:${ ARRAY_NAME[INDEX] }
声明数组:declare -a ARRAY_NAME
declare -A ARRAY_NAME:关联数组:
数组中元素的赋值:
1)一次只赋值一个元素:ARRAY_NAME[INDEX]=VALUE,weekday[0]=‘Sunday’
2)一次赋值全部元素:ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)
3)只赋值特定元素:ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
4)read -a ARRAY
引用数组元素:$(ARRAY_NAME[INDEX]}
注意:省略[INDEX]表示引用下标为0的元素;
所有元素:${ARRAY_NAME[@]},${ARRAY_NAME[*]}
${ARRAY_NAME[@]:offset:number}:offset,要跳过的元素个数,number,要取的元素个数,切片(类似python中的切片)
数组的长度(数组中元素的个数):${#ARRAY_NAME[*]},${#ARRAY_NAME[@]}
向数组中增加元素:
ARRAY[${#ARRAY[*]}]
删除数组中的某元素:
unset ARRAY[INDEX]
关联数组:
declare -A ARRAY_NAME
ARRAY_NAME=([index_name1]='val1' [index_name2]='val2' ... )
bash的字符串处理工具:
字符串切片:${var:offset:number}
取字符串的最右侧几个字符:${var: -length},注意,冒号后必须有一个空白字符
基于模式取子串:
${var#*word}:word可以是指定的任意字符,功能:自左而右,查找var变量所存储的字符串中,第一次出现的word,删除字符串开头至第一次出现word字符之间的所有字符;
${var##*word}:同上,不过删除的是字符串开头至最后一次由word指定字符串之间的所有内容;
${var%word*}:word可以是指定的任意字符,功能:自右而左,查找var变量所存储的字符串中,第一次出现的word,删除字符串最后一个字符向左至第一次出现word字符之间的所有字符;
${var%%word*}:同上,只不过删除字符串最右侧的字符向左至最后一次出现word字符之间的所有字符;
查找替换:
${var/pattern/substi}:查找var所表示的字符串中,第一次被pattern所匹配到的字符串,以substi替换之;
${var//pattern/substi}:查找var所表示的字符串中,所有能被pattern所匹配到的字符串,以substi替换之;
${var/#pattern/substi}:查找var所表示的字符串中,行首被pattern所匹配到的字符串,以substi替换之;
${var/%pattern/substi}:查找var所表示的字符串中,行尾被pattern所匹配到的字符串,以substi替换之;
查找并删除:
${var/pattern}:查找var所表示的字符串中,第一次被pattern所匹配到的字符串,删除之;
${var//pattern}:查找var所表示的字符串中,所有被pattern所匹配到的字符串,删除之;
${var/#pattern}:查找var所表示的字符串中,行首被pattern所匹配到的字符串,删除之;
${var/%pattern}:查找var所表示的字符串中,行尾被pattern所匹配到的字符串,删除之;
字符大小写转换:
${var^^}:把var中的所有小写字符串转换为大写;
${var,,}:把var中的所有大写字符串转换为小写;
变量赋值:
${var:-value}:如果var为空或未设置,那么返回value,否则返回var的值;
${var:=value}:如果var为空或未设置,,返回value,并赋值value给var,否则,返回var的值;
${var:+value}:如果var不空,那么返回value;
${var:?error_infp}:果var为空或未设置,那么返回error_info,至错误流,否则,返回var的值;
为脚本程序使用配置文件:
.(点号)或source
1)定义文本文件,每行定义“name=value”
2)在脚本中source此文件即可
/tmp/host.cfg:
HOSTNAME='www.ceshitest.com'
#!/bin/bash
#
[ -r /tmp/host.cfg ] && source /tmp/host.cfg # source可用.(点号)代替
HOSTNAME=${HOSTNAME:-www.cd.com}
hostname $HOSTNAME
感觉有点类似宏替换
命令:
mktemp命令:mktemp [OPTION] ... [TEMPLATE]
TEMPLATE:filename.XXX,X至少出现三个
-d:创建临时目录
--tmpdir:指定临时目录位置:--tmpdir=/PATH/TO/SOMEDIR
mktemp /tmp/test.XXX,
install命令:
install [OPTION]... [-T] SOURCE DEST
install [OPTION]...SOURCE... DIRECTORY
install [OPTION]...-t DIRECTORY SOURCE
install [OPTION]...-d DIRECTORY...
选项:直接指定创建目标的权限,属主、属组
-m MODE
-o OWNER
-g GROUP