- 以 #!解析器名称 开头,表示选择哪个解释器解释shell脚本
- source命令
- export命令
- env命令
- unset命令
第二章 shell编程基础 - 函数传递
- 标准输入输出符号
第三章 编程的基本元素 - 获取命令执行返回值
- $((数值计算)) 获取数值计算结果 如:$((100 - 2*100/400))
- 替换运算符
- 模式匹配运算符
- shift命令可以截取参数列表最左端的一个参数
- type命令判断被执行命令的来源(别名、关键字、函数、内置命令、外部命令)
- test命令用于评估表达式,返回零(true)或者非零(false)
- 逻辑运算符
- 运算符号
- 执行反单引号(`)之间的命令,引用结果作为字符串
- getopt命令用于分析命令标志和参数
第四章 正则表达式 - 元字符
- POSIX字符集
- 后向引用
- 交替、分组
第五章 基本文本处理 - 排序文本
- 文本去重
- 统计文本
- 打印和格式化输出
- 提取文本开头和结尾
- 字段处理
- 文本替换
- Linux下的配置文件
第六章 文件和文件系统 - 文件
- 文件系统
第七章 流编辑器sed - 工作地址范围
- 命令
- [:特殊字符:]用于匹配特俗字符
第八章 文本处理利器awk - awk代码结构
- 内建变量
- 局部变量
- 字符串函数
第九章 进程 - 进程管理命令
- init进程
- 调度系统任务
- 加载proc虚拟文件系统:mount -t proc proc /proc
第十章 OpenSSH - 安装openssh
- 使用SSH登录远程主机(要求远程主机正在运行sshd)
第十一章 实用程序(日志处理程序、系统监控程序) - 日志清理程序
- 系统监控程序
第一个Shell程序
shell编程基础
编程的基本元素
- $(命令) 如:$(date +%Y%m%d%H%M%S)
命令
如:echo 123
- ${varname:-word}
如果varname存在且非null,则返回varname的值;否则,返回word - ${varname:=word}
如果varname存在且非null,则返回varname的值;否则,将varname的值设置为word,然后返回word - ${varname:?message}
如果varname存在且非null,则返回varname的值;否则打印message,并退出当前脚本 - ${varname:+word}
如果varname存在且非null,则返回word;否则返回null
- ${varname#pattern}
如果模式匹配变量取值的开头处,则删除匹配的最短部分,并返回剩下部分 - ${varname##pattern}
如果模式匹配变量取值的开头处,则删除匹配的最长部分,并返回剩下部分 - ${varname%pattern}
如果模式匹配变量取值的结尾处,则删除匹配的最短部分,并返回剩下部分 - ${varname%%pattern}
如果模式匹配变量取值的结尾处,则删除匹配的最长部分,并返回剩下部分 - ${varname/pattern/string} ${varname//pattern/string}
将varname中匹配模式的最长部分替换为string
第一种格式中,只有匹配的第一部分被替换,第二种格式中,varname中所有匹配的部分都被替换
如果模式以#开头,则必须匹配varname的开头,如果模式以%开头,则必须匹配varname的结尾
- 关于文件与目录的侦测逻辑卷标!
-f常用!侦测『文件』是否存在 eg: if [ -f filename ]
-d常用!侦测『目录』是否存在
-b侦测是否为一个『 block 文件』
-c侦测是否为一个『 character 文件』
-S侦测是否为一个『 socket 标签文件』
-L侦测是否为一个『 symbolic link 的文件』
-e侦测『某个东西』是否存在! - 关于程序的逻辑卷标!
-G侦测是否由 GID 所执行的程序所拥有
-O侦测是否由 UID 所执行的程序所拥有
-p侦测是否为程序间传送信息的 name pipe 或是 FIFO - 关于文件的属性侦测!
-r侦测是否为可读的属性
-w侦测是否为可以写入的属性
-x侦测是否为可执行的属性
-s侦测是否为『非空白文件』
-u侦测是否具有『 SUID 』的属性
-g侦测是否具有『 SGID 』的属性
-k侦测是否具有『 sticky bit 』的属性 - 两个文件之间的判断与比较 ;例如[ test file1 -nt file2 ]
-nt第一个文件比第二个文件新
-ot第一个文件比第二个文件旧
-ef第一个文件与第二个文件为同一个文件( link 之类的文件) - 逻辑的『和(and)』『或(or)』
&&逻辑的 AND 的意思
||逻辑的 OR 的意思
正则表达式
- 匹配前面正则表达式的一个或多个实例
- 匹配零个或多个前面字符
| 匹配前面或后面的正则表达式
() 匹配括号括起来的正则表达式
\b 单词锁定符,代表单词的开头和结尾,即单词的分界处
\B 匹配两个单词组成字符间的空字符串
< > 分别匹配单词开头和单词结尾
\n 换行符
\d 匹配一位数字
\w 匹配文字和数字字符
\W 匹配一个或多个非单词字符
基本文本处理
文件和文件系统
流编辑器(sed)
- 行地址 如:删除第一行 sed -e '1d' filePath
- 行范围地址 如:打印1到5行 sed -n -e '1,5p' filePath
- 正则表达式地址 如:打印所有以开头的注释行 sed -n -e '/^#/p' filePath
- 两个用逗号分开的正则表达式之间的地址 如:打印从包含'BEGIN'的行开始,并且包含'END'的行结束的文本块 sed -n -e '/BEGIN/,/END/p' filePath
- d 删除
- p 打印
- s/// 替换
- = 打印行号
- i 插入
- a 追加
文本处理利器(awk)
- 处理输入前的初始化
BEGIN{
....
} - 处理输入过程
[ 条件 ] {
...
} - 处理完所有输入后的扫尾工作
END{
...
}
进程
- /etc/inittab init程序读取的配置文件
基本格式为 id:runlevels:action:process- id为1~2个字符,配置行的唯一标识,在配置文件中不能重复
- runlevels(运行等级)取值如下
- 等级0表示:表示关机(千万不能把initdefault 设置为0)
- 等级1表示:单用户模式
- 等级2表示:无网络连接的多用户命令行模式
- 等级3表示:有网络连接的多用户命令行模式
- 等级4表示:不可用
- 等级5表示:带图形界面的多用户模式
- 等级6表示:重新启动(千万不要把initdefault 设置为6)
- action取值如下
- respawn 启动并监视第4项指定的process,若process终止则重启它
- wait 执行第4项指定的process,并等待它执行完毕
- once 执行第4项指定的process
- boot 不论在哪个执行等级,系统启动时都会运行第4项指定的process
- bootwait 不论在哪个执行等级,系统启动时都会运行第4项指定的process,且一直等它执行完备
- off 关闭任何动作,相当于忽略该配置行
- ondemand 进入ondemand执行等级时,执行第4项指定的process
- initdefault 系统启动后进入的执行等级,该行不需要指定process
- sysinit 不论在哪个执行等级,系统会在执行boot 及bootwait之前执行第4项指定的process
- powerwait 当系统的供电不足时执行第4项指定的 process,且一直等它执行完毕
- powerokwait 当系统的供电恢复正常时执行第4项指定的process,且一直等它执行完毕
- powerfailnow 当系统的供电严重不足时执行第4项指定的process
- ctrlaltdel 用户按下【Ctrl+Alt+Del】时执行的操作
- kbrequest 当用户按下特殊的组合键时执行第4项指定的process,此组合键需在keymaps文件定义
- process为所要执行的shell命令。任何合法的shell语法均适用于该字段。
- /etc/rc.d/rcX.d文件(X代表运行等级)含有各个运行等级服务启动和终止配置
/etc/rc.d/init.d目录下存放着对应运行等级的服务脚本 - 修改配置/etc/inittab后马上生效:kill -1 1 或者 init q
- crontab命令用于调度重复性的任务
控制访问的文件:cron.allow、cron.deny - at命令用于调度只执行一次的任务
控制访问的文件:at.deny
SSH
替换阿里云的源
echo "http://mirrors.aliyun.com/alpine/latest-stable/main/" > /etc/apk/repositories
echo "http://mirrors.aliyun.com/alpine/latest-stable/community/" >> /etc/apk/repositories同步时间
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime更新源、安装openssh 并修改配置文件、生成key、启动sshd服务
apk update &&
apk add --no-cache openssh-server tzdata &&
sed -i "s/#PermitRootLogin.*/PermitRootLogin yes/g" /etc/ssh/sshd_config &&
ssh-keygen -t rsa -P "" -f /etc/ssh/ssh_host_rsa_key &&
ssh-keygen -t ecdsa -P "" -f /etc/ssh/ssh_host_ecdsa_key &&
ssh-keygen -t ed25519 -P "" -f /etc/ssh/ssh_host_ed25519_key &&
/usr/sbin/sshd -D
基于口令的登录方法
ssh -l 登录账号 远程主机基于密钥对的登录方法
- 生成密钥对:ssh-keygen -d
- 把公钥上传到服务器上(/.ssh/authorized_keys)
- 测试自动登录:ssh [登录账号@]远程主机 (需要输入第一步输入的passphrase,若想不输入passphrase可以考虑ssh-agent)
实用程序
# maximum log size
alarmrate=500
# the max size file can reach
file_max_size=5
# this is the directory where fresh logs are originally written
working_dir=/mnt/soho_storage/log
# this is the frequency our program runs
SLEEPTIME=5
# append year.month.day and timestamp to log filename
filenameConvert()
{
timestamp=$(date +%Y%m%d%H%M%S)
timestamp=`echo $timestamp`
RETVAL=$1.$timestamp
}
# search dir to fetch the oldest log
searchdir()
{
oldestlog=`ls -rt | head -n 1 | awk '{print $1}'`
}
# this function clean old logs under working dir if it reaches it's size limitation
clear_old_log_under_working_dir()
{
cd $working_dir
while true
do
logsize=`du -ms $working_dir | awk '{print $1}'`
if [ $logsize -gt $alarmrate ]
then
searchdir
rm -rf $oldestlog
else
break;
fi
done
}
# this is the main process of our log backup activity
backuplog_process()
{
cd $log_ram_dir
for i in *
do
file_size=`du -m $i | awk '{print $1}'`
# need to backup log file
case $i in access.log | error.log | apcupsd.events | soho.log)
if [ ! -d $working_dir ]
then
mkdir -p $working_dir
fi
if [ file_size -gt file_max_size ]
then
filenameConvert $i
cp $log_ram_dir/$i $working_dir/$RETVAL
echo "" > $log_ram_dir/$i
clear_old_log_under_working_dir
fi
;;
*)
if [ file_size -gt file_max_size ]
then
echo "" > $log_ram_dir/$i
fi
done
}
while true
do
backuplog_process
sleep $SLEEPTIME
done
# maximum ratio of memory usage
mem_quota=80
# hard disk
hd_path=/dev/sda1
# maximum ratio of hard disk usage
hd_quota=80
# maximum ratio of cpu usage
cpu_quota=80
# time gap between two times fetching cpu status
time_gap=60
# generate report every 10 minutes
runtime_gap=60
# fetch the ratio of memory usage
# @return 1: if larger than $mem_quota
0: if less than $mem_quota
watch_memory()
{
mem_total=`cat /proc/meminfo | grep MemTotal | awk '{print $2}'`
mem_free=`cat /proc/meminfo | grep MemFree | awk '{print $2}'`
mem_usage=$((100-mem_free*100/mem_total))
if [ $mem_usage -gt $mem_quota ]
then
mem_message="ALARM!! The memory usage is $mem_usage%!!"
return 1
else
return 0
fi
}
# fetch the top 10 most wasting memory process
proc_memory_top10()
{
mem_busiest=`ps aux | sort -nk 4r | head -n 11`
}
# fetch the ratio of hard disk usage
# @return 1: if larger than $hd_quota
0: if less than $hd_quota
watch_hd()
{
hd_usage=`df | grep $hd_path | awk '{print $5}' | sed 's/%//g'`
if [ $hd_usage -gt $hd_quota ]
then
hd_message="ALARM!! The hard disk usage is $hd_usage%!!"
return 1
else
return 0
fi
}
# fetch cpu status at a time point
# format used unused
get_cpu_info()
{
cat /proc/stat | grep -i "^cpu[0-9]\+" | awk '{used+=$2+$3+$4;unused+=$5+$6+$7+$8} END{print used,unused}'
}
# fetch the ratio of cpu usage
# fetch cpu stat two times, with time gap, then calculate the average status
# @return 1: if larger than $cpu_quota
0: if less than $cpu_quota
watch_cpu()
{
time_point_1=`get_cpu_info`
sleep $time_gap
time_point_2=`get_cpu_info`
cpu_usage=`echo $time_point_1 $time_point_2 | awk '{used=$3-$1;total+=$3+$4-$2-$1}';print $used*100/total`
if [ $cpu_usage -gt $cpu_quota ]
then
cpu_message="ALARM!! The cpu usage is over $cpu_usage%!!"
return 1
else
return 0
fi
}
# fetch the top 10 busiest processes
proc_cpu_top10()
{
proc_busiest=`ps aux | sort -nk 3r | head -n 11`
}
while true
do
# report content
report=""
# memory monitor
if [ `watch_memory` -eq 1 ]
then
report=$report'\n'$mem_message
proc_memory_top10
report=$report'\n'$mem_busiest
fi
# hard disk monitor
if [ `watch_hd` -eq 1 ]
then
report=$report'\n'$hd_message
fi
# cpu monitor
if [ `watch_cpu` -eq 1 ]
then
report=$report'\n'$cpu_message
proc_cpu_top10
report=$report'\n'$proc_busiest
fi
# feedback report
if [ -n $report ]
then
# todo...
fi
# sleep time
sleep $((runtime_gap-time_gap))
done