vi是交互式的编辑工具,而sed(Stream Editor) 是一个行编辑器,一般格式为sed [option]... '定界编辑' file...
,既可以使用双引号也可以使用单引号,如果要进行变量替换则要使用双引号。
sed每次读取一行,不会编辑原文件,基于编辑脚本对模式空间中的内容进行编辑,把处理后的结果输出至屏幕。
常用选项
-n:静默模式,不输出模式空间中的内容至屏幕,sed默认会把读入的一行输出到标准输出,修改的结果也会输出。
-e: 多点编辑 -e 'script' -e 'script'
-f /PATH/TO/SCRIPT_FILE: 从指定文件中读取编辑脚本
-r: 使用扩展正则表达式
-i: 原处编辑
地址定界
(1)不给范围:对全文进行处理
(2)单地址: #指定的行 | /pattern/:被此处模式所能够匹配到的每一行;
(3)地址范围:#,# | #,+# | /pat1/,/pat2/ | #,/pat1/
sed的编辑命令
d: 删除 如sed '1,3d' /etc/fstab | sed ‘/^#/d’ /etc/fstab | sed ‘/^//d’ /etc/fstab
p: 显示模式空间中的内容,所以能被匹配到的显示两次,不能被匹配到的显示一次。和-n合用只显示匹配到的行
a \text:在行后面追加文本;支持使用\n实现多行追加;
i \text:在行前面插入文本;支持使用\n实现多行插入;
c \text:替换行为单行或多行文本;
w /path/to/somefile: 将符合条件的所有行保存到指定的文件中,覆盖式保存。
r /path/from/somefile:在指定位置把另外一个文件的内容插入进来
=: 显示符合条件的行的行号
!: 对地址定界取反 ;
s/要查找的内容/要替换的内容/:查找替换,支持使用其它分隔符,s@@@,s###。查找条件可以使用模式,替换不可使用模式。sed ‘地址定界s@查找条件@替换文本’ file。地址定界就表示只对指定范围内行做查找。
替换标记
g: 行内全局替换;
i:匹配时不区分字符大小写
p: 显示替换成功的行;
w /PATH/TO/SOMEFILE:将替换成功的结果保存至指定文件中;
删除/boot/grub/grub.conf文件中所有以空白开头的行行首的所有空白字符;
sed 's@^[[:space:]]\{1,\}@@' /boot/grub/grub.conf
删除/etc/fstab文件中所有以#开头,后面至少跟一个空白字符的行的行首的#和空白字符;
sed 's@^#[[:space:]]\+@@' /etc/fstab
echo一个绝对路径给sed命令,取出其基名;取出其目录名;
echo "/etc/sysconfig/" | sed 's@[^/]\+/\?$@@'
替换/etc/inittab文件中”id:3:initdefault:"一行中的数字为5
sed 's@\(id:\)[0-9]\(:initdefault\)@\15\2' /etc/inittab
删除/etc/init.d/funcions文件中的空白行
sed "/^$/d" /etc/init.d/funcions
删除/etc/inittab文件中位于行首的#
sed 's@^#@@g' /etc/inittab
删除/etc/rc.d/rc.sysinit文件中以#后跟至少一个空白字符开头的行的行首的#和空白字符
sed "s@^#[[:space:]]\{1,\}\@@g" /etc/rc.d/rc.sysinit
取出一个文件路径的目录名称,如/etc/sysconfig/network,其目录为/etc/sysconfig,功能类似dirname命令;
echo /etc/sysconfig/network | sed 's@[^/]/{1,\}\/?$@@'
高级编辑命令:
h: 把模式空间中的内容覆盖至保持空间中; H:把模式空间中的内容追加至保持空间中; g: 从保持空间取出数据覆盖至模式空间; G:从保持空间取出内容追加至模式空间; x: 把模式空间中的内容与保持空间中的内容进行互换; n: 读取匹配到的行的下一行至模式空间; N:追加匹配到的行的下一行至模式空间; d: 删除模式空间中的行; D:删除多行模式空间中的所有行;
until的使用格式
until CONDITION; do
循环体
done
进入条件:CONDITION为false
退出条件:CONDITION为true
求100以内所有正整数之和
#!/bin/bash
declare -i i=1
declart -i sum=0
until [ $i -gt 100 ];do
let sum+=$i
let i++
done
echo "sumary:$sum"
打印九九乘法表
#!/bin/bash
declare -i j=1
declare -i i=1
until [ $j -gt 9 ]; do
until [ $i -gt $j ]; do
echo -n -e "${i}X${j}=$[$i*$j]\t"
let i++
done
echo
let i=1
let j++
done
continue和break用于循环体中
continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断
while CONDTIITON1; do
CMD1
...
if CONDITION2; then
continue
fi
CMDn
...
done
break [N]:提前结束循环
while CONDTIITON1; do
CMD1
...
if CONDITION2; then
break
fi
CMDn
...
done
求100以内所有偶数之和;要求循环遍历100以内的所正整数;
#!/bin/bash
declare -i i=0
declare -i sum=0
until [ $i -gt 100 ];do
let i++
if [ $[$i%2] -eq 1 ];then
continue
fi
let sum+=$i
done
echo 'even sum:$sum'
whhile true; do
循环体
done
--------------------------------------------------------
until false; do
循环体
done
每隔3秒钟到系统上获取已经登录的用户的信息;如果docker登录了,则记录于日志中,并退出;
#!/bin/bash
read -p "ender a user name:" username
while true;do
if who | grep "^$username" &> /dev/null;then
break
fi
sleep 3
done
echo "$username logged in." >> /tmp/user.log
-----------------------------------------------------------------------
#!/bin/bash
read -p "ender a user name:" username
until who | grep "^$username" &> /dev/null;do
sleep 3
done
echo "$username logged in." >> /tmp/user.log
while循环还可以用来遍历文件的每一行,依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line:
while read line; do
循环体
done < /PATH/FROM/SOMEFILE
找出其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 ((控制变量初始化;条件判断表达式;控制变量的修正表达式)); do
循环体
done
求100以内所正整数之和;
#!/bin/bash
declare -i sum=0
for ((i=1;i<=100;i++)); do
let sum+=$i
done
echo "Sum: $sum."
打印九九乘法表;
#!/bin/bash
for((j=1;j<=9;j++));do
for((i=1;i<=j;i++))do
echo -e -n "${i}X${j}=$[$i*$j]\t"
done
echo
done
写一个脚本,完成如下任务 (1) 显示一个如下菜单: cpu) show cpu information; mem) show memory information; disk) show disk information; quit) quit (2) 提示用户选择选项; (3) 显示用户选择的内容;
#!/bin/bash
cat << EOF
cpu) show cpu information;
mem) show memory information;
disk) show disk information;
quit) quit
EOF
========
read -p "enter an option:" option
while [ "$option" != 'cpu' -a "$option" != 'mem' -a "$option" != 'disk' -a "$option" != 'quit' ]; do
read -p "Wrong option, Enter again: " option
done
if [ "$option" == 'cpu' ]; then
lscpu
elif [ "$option" == 'mem' ]; then
cat /proc/meminfo
elif [ "$option" == 'disk' ]; then
fdisk -l
else
echo "Quit"
exit 0
fi
进一步地:用户选择,并显示完成后不退出脚本;而是提示用户继续选择显示其它内容;直到使用quit方始退出;
case支持glob风格的通配符:
*: 任意长度任意字符;
?: 任意单个字符;
[]:指定范围内的任意单个字符;
a|b: a或b
case 变量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
...
*)
默认分支
;;
esac
#!/bin/bash
cat << EOF
cpu) show cpu information;
mem) show memory information;
disk) show disk information;
quit) quit
EOF
========
read -p "enter an option:" option
while [ "$option" != 'cpu' -a "$option" != 'mem' -a "$option" != 'disk' -a "$option" != 'quit' ]; do
read -p "Wrong option, Enter again: " option
done
case "$option" in
cpu)
lscpu
;;
mem)
cat /proc/meminfo
;;
disk)
fdisk -l
;;
*)
echo "Quit..."
exit 0
;;
esac
函数是一个功能模块,是一段代码片段,可以在代码片段中调用函数,函数是过程式编程中实现代码重用的方法,也是结构化编程的基本保证。函数只有被调用才可以执行,函数名出现的地方,会被自动替换为函数代码。函数的调用会引入一个新的执行环境,称为函数的上下文。
函数定义语法:
语法一
function f_name{
...函数体...
}
语法二
f_name(){
...函数体...
}
函数有生命周期,被调用时创建,返回时终止。return返回的是自定义状态结果,范围是0-255,0表示执行成功,其他是失败。
函数可以接受参数,调用函数时,在函数名后面以空白分隔给定参数列表即可;例如“testfunc arg1 arg2 ...”
在函数体中当中,可使用$1,
添加10个用户
#!/bin/bash
function adduser {
if [ $# -lt 1 ]; then
return 2
# 2: no arguments
fi
if id $1 &> /dev/null; then
echo "$1 exists."
return 1
else
useradd $1
[ $? -eq 0 ] && echo "Add $1 finished." && return 0
fi
}
for i in {1..10}; do
adduser myuser$i
done
写一个脚本,完成如下要求 (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...”; 如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“SCRIPT_NAME is stopped...”;
其中:SCRIPT_NAME为当前脚本名;
#!/bin/bash
# chkconfig: - 88 12
# description: test service script
prog=$(basename $0)
lockfile=/var/lock/subsys/$prog
start() {
if [ -e $lockfile ]; then
echo "$prog is aleady 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
}
usage() {
echo "Usage: $prog {start|stop|restart|status}"
}
if [ $# -lt 1 ]; then
usage
exit 1
fi
case $1 in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
status)
status
;;
*)
usage
esac
函数的执行结果返回值:
(1) 使用echo或print命令进行输出;
(2) 函数体中调用命令的执行结果;
函数的退出状态码:
(1) 默认取决于函数体中执行的最后一条命令的退出状态码;
(2) 自定义退出状态码:return
本地变量:当前shell进程;为了执行脚本会启动专用的shell进程;因此,本地变量的作用范围是当前shell脚本程序文件;
局部变量:函数的生命周期;函数结束时变量被自动销毁;在函数中定义局部变量的方法:local NAME=VALUE
###函数递归
函数直接或间接调用自身;
阶乘
#!/bin/bash
fact() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
echo 1
else
echo $[$1*$(fact $[$1-1])]
fi
}
fact 5
求n阶斐波那契数列;
#!/bin/bash
fab() {
if [ $1 -eq 1 ]; then
echo 1
elif [ $1 -eq 2 ]; then
echo 1
else
echo $[$(fab $[$1-1])+$(fab $[$1-2])]
fi
}
fab 7