shell脚本

Shell是一个命令解释器。它不仅是操作系统内核与用户之间的绝缘层,同时也是一种功能相当强大的编程语言。一个Shell程序,通常称为脚本,它是一个由系统调用,命令工具,软件包和已编译的二进制包"粘合" 起来的极易使用的工具。事实上,整个UNIX系统命令,软件包和工具都能由一个shell脚本调用。如果这还不够,Shell的内部命令,比如测试和循环结构,都使Shell脚本更强大和更有弹性。Shell脚本在系统管理任务表现非常的出色,并且对于日常反复性的处理工作避免了使用那些结构过于复杂的程序语言。

分支语句

num=100
if [ $num -lt 60 ]
then
    echo "C"
elif [ $num -lt 80 ] && [ $num -ge 60 ]
then
    echo "B"
elif [[ $num -lt 90 && $num -ge 80 ]]
then
    echo "A"
elif (( $num <= 100 )) && (( $num >= 90 ))
then
    echo "A+"
else
    echo "NA"
fi
stage=A
case $stage in
    A)
        echo "90-100"
        ;;
    B)
        echo "80-90"
        ;;
    C)
        echo "60-80"
        ;;
    *)
        echo "BAD"
        ;;
esac

循环语句

for语句

for x in one two three four
do
    echo number $x
done
for x in /var/log/*
do
    echo $(basename $x) is a file living in /var/log
done
for j in $(seq 1 5)
do
    echo $j
done
for (( i=1; i<=5; i++ ))
do
    echo "i=$i"
done

while语句

i=1
while [ $i -le 10 ]
do
    echo $i
    i=`expr $i + 1`;
    # let i+=1;
    # ((i++));
    # i=$[$i+1];
    # i=$(( $i + 1 ))
done

until语句

i=1
until [ $i -gt 10 ]
do
    echo $i
    i=$(( $i + 1 ))
done

文本处理工具

grep

grep -c <pattern> <filename>            # 统计出现次数
grep -v <pattern> <filename>            # 匹配取反
grep -E <pattern1>|<pattern2> <filename> # or
grep -E <pattern1>.*<pattern2> <filename> # and
grep -w <pattern> <filename>            # 按单词边界查找,非常好用

sed

关于sed的使用,强烈推荐阅读文档:http://www.grymoire.com/Unix/Sed.html。本文是该文档的摘录。另外必须要熟悉sed正则表达式

sed一般采用/作为分界符,分界符可以用其它字符,例如要操作的字符串包含/,那么可以用:作为分界符来避免引入转义字符。

sed的语法结构为:

sed options 'addr cmd/arg1/arg2/flags' filename
  1. 常用选项

    -E 扩展正则表达式
    -n 只输出匹配的行
    -e 脚本表达法,可以执行多条命令
    -f 指定脚本名字
    -i 就地处理,不产生副本
  2. addr写法

    number                                  # match the only number line
    $                                       # match last line
    /regexp/                                # match regexp
    addr1,addr2                             # [addr1, addr2]
    addr1,+N                                # [addr1, addr1+N]
    
  3. 常用命令

    命令 功能
    = 打印匹配行号
    a\text 将文本插入匹配行之后
    i\text 将文本插入匹配行之前
    c\text 将匹配行替换为制定文本
    d 删除匹配行
    p 打印匹配行
    s/expr1/expr2/ 将匹配行expr1替换为expr2
    y/expr1/expr2/ 将匹配行中expr1中字符替换为expr2中字符,没什么用
  4. 常用标志

    g 作用于全局
    I 忽略大小写
    p 打印
    w fname 输出到文件

可以用如下的方式来写sed脚本,如果要指定选项,必须将选项放到-f之前:

#!/bin/sed -nf
s/a/A/g
s/e/E/g

文本替换

sed 's/src/dst/flags' fname > fname.sed

在sed中,特殊符号&用于引用匹配字符串,在下面例子中要注意贪婪算法。

1: echo "123 abc" | sed 's/[0-9]*/& &/'    # 123 123 abc
2: echo "abc 123" | sed 's/[0-9]*/& &/'    # abc 123
3: echo "abc 123" | sed -E 's/[0-9]+/& &/' # abc 123 123

分组表达式是非常重要的特性,每个小括号创建一个分组,用\1\2等引用分组,最多可引用分组为9个。

echo hello123 | sed -E 's/([a-z]*).*/\1/'               # hello
echo hello world | sed -E 's/([a-z]+) ([a-z]+)/\2 \1/'  # world hello
echo hello hello world | sed -E 's/([a-z]+) \1/\1/'     # hello world

如果只想修改第n次出现,可以用如下表达式:

echo 1b 2b 3b 4b | sed -E 's/b/?/3'     # 1b 2b 3? 4b
echo 1b 2b 3b 4b | sed -E 's/b/?/3g'    # 1b 2b 3? 4?

指定行号范围:

sed -nE '10,20 p'                       # print line 10-20
sed -nE '10,20 !p'                      # print line not in 10-20
sed -nE '/^#/ s/[0-9]+//gp'             # del num in #line
sed -nE '10,/^#/ s/[0-9]+//gp'          # del num from 10line to #line
sed -nE '10,/^#/ d'                     # del line from 10line to #line
sed -nE '10,/^#/ !d'                    # del line not in 10line to #line

实用操作

# Windows line to Unix(remove ^M)
sed -i 's/\r//' fname

# upper to lower
sed 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' fname

# grep line begin with cp or scp
sed -n '/^s*cp / p' fname
sed -n '/^s*cp / =' fname                       # print line-number only

# delete line begin with cp or scp
sed -n '/^s*cp / d' fname

# del begin whitespace in line
sed -i 's/^[ \t]*//g' fname

# del trailing whitespace in line
sed -i 's/[ \t]*$//g' fname

echo world | sed '/world/ i\hello '             # insert before world-line
echo world | sed '/world/ a\hello \nend'        # append after world-line
echo world | sed '/world/ c\hello '             # change line to hello-line
ls -lF | sed -nE '/^total\ |.*\/$/ !p'          # show file except directory
#!/bin/bash

fname=$1
function del_cpp_comment()
{
    # del //... in line begin
    sed -i '/^[ \t]*\/\// d' $fname

    # del //... in line end
    sed -i "s/[ \t]*\/\/[^\"]*$//" $fname

    # del single line only have /*...*/
    sed -i '/^[ \t]*\/\*.*\*\/[ \t]*$/ d' $fname

    # del single line /*...*/
    sed -i 's/[ \t]*\/\*.*\*\///' $fname

    # del multi line /*...*/
    sed -i '/^[ \t]*\/\*/,/.*\*\// d' $fname
}
del_cpp_comment

awk

参考教程:http://www.grymoire.com/Unix/Awk.html

sed的强项是自动化编辑文本,awk的强项是分析数据生成报告。相比于sed,awk更像是一门语言,和C语言有很相似的语法结构。 awk的默认工作流程是读取每一条记录,按照空格分解为子项目,用$1$2这样的方式引用子项,注意$0表示所有子项。可以用-F ':'这样的选项将默认的空格分隔符改变为使用冒号作为分隔符。 awk有许多内置变量,并且可以被修改:

ARGC 命令行参数个数
ARGV 命令行参数排列
ENVIRON 支持队列中系统环境变量的使用
FILENAME awk浏览的文件名
FNR 浏览文件的记录数
FS 设置输入域分隔符,等价于命令行-F选项
NF 浏览记录的域的个数
NR 已读的记录数
OFS 输出域分隔符
ORS 输出记录分隔符
RS 控制记录分隔符
ls -l | awk '{print $3,$9}'
cat /etc/passwd | awk -F ':' 'BEGIN {print "name\tshell"}{print $1"\t"$7}'
awk -F ':' '{printf("file:%10s %s %s: %s\n",FILENAME,NR,NF,$0)}' /etc/passwd

系统工具

系统性能分析

top

该指令的命令行格式为:

top -hv|-bcHiOSs -d secs -n max -u|U user -p pid -o fld -w [cols]
h/v 帮助/版本信息
b 批量模式
d 延迟时间:如0.1表示0.1s
H 线程单独显示,而不是依附到进程内部
i 不显示空闲进程
n 刷新指定帧数之后退出
o 用于更改排序优先级,+降低,-增加
p 指定观察进程
top - 09:38:06 up 4 days, 16:58,  7 users,  load average: 0.72, 0.23, 0.19
Tasks: 251 total,   3 running, 248 sleeping,   0 stopped,   0 zombie
%Cpu(s): 26.7 us,  2.2 sy, 44.1 ni,  1.5 id, 25.3 wa,  0.0 hi,  0.2 si,  0.0 st
KiB Mem:   5998076 total,  5797400 used,   200676 free,   127412 buffers
KiB Swap:  7916540 total,    28824 used,  7887716 free.  3143600 cached Mem
user/us
用户进程使用占比
sys/sy
内核进程使用占比
nic/ni
nice用户进程使用占比
idle/id
空闲时间占比,如果这个值过低,表示CPU存在瓶颈
io/wa
等待IO的占比,如果这个值过高,表示IO存在瓶颈
irq/hi
中断占比
sirq/si
软中断占比
steal/st
虚拟CPU使用占比,一般在虚拟机中才能看到
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 1286 root      20   0  362848  63316  32652 S   1.0  1.1  34:57.79 Xorg
 1855 micky     20   0 1136568  84168  24656 S   1.0  1.4  49:49.06 compiz
23997 micky     20   0  556880  94936  18764 S   0.7  1.6   0:26.23 emacs
   54 root      20   0       0      0      0 S   0.3  0.0   0:38.91 kworker/1:1
23969 micky     20   0  593700  23772  13896 S   0.3  0.4   0:02.94 gnome-terminal
24492 micky     20   0   29168   1768   1184 R   0.3  0.0   0:00.03 top
PID
进程ID
USER
effective USER名
PR
优先级
NI
nice值
VIRT
虚拟内存大小
RES
驻留内存大小,即使用的不可交换物理内存
SHR
共享内存大小
%CPU
CPU占用率
%MEM
MEM占用率
TIME
已执行时间
COMMAND
命令行

此外top命令还有很多交互式命令,如下所示:

4a. Global-Commands
      <Ent/Sp> ?, =, 0,
      A, B, d, E, e, g, h, H, I, k, q, r, s, W, X, Y, Z
4b. Summary-Area-Commands
      C, l, t, m, 1, 2, 3
4c. Task-Area-Commands
      Appearance:  b, J, j, x, y, z
      Content:     c, f, F, o, O, S, u, U, V
      Size:        #, i, n
      Sorting:     <, >, f, F, R
4d. Color-Mapping
      <Ret>, a, B, b, H, M, q, S, T, w, z, 0 - 7
5b. Commands-for-Windows
      -, _, =, +, A, a, g, G, w
5c. Scrolling-a-Window
      C, Up, Dn, Left, Right, PgUp, PgDn, Home, End
5d. Searching-in-a-Window
      L, &

这里介绍几个实用的命令:

M
进程列表按内存使用大小降序排序
P
进程列表按CPU使用大小降序排序

free

free命令的数据都是从/proc/meminfo中读取的。

$ free
             total       used       free     shared    buffers     cached
Mem:       5998076    5771648     226428     437208     372552    3365820
-/+ buffers/cache:    2033276    3964800
Swap:      7916540     263972    7652568
Mem
从操作系统来看,显示的是物理内存, buffers表示要写入到磁盘的数据,cached表示从磁盘缓存的数据,可以用echo 3 > /proc/sys/vm/drop_caches清除缓存

vmstat

用于统计虚拟内存信息:

vmstat [options] [delay [count]]
a 显示active/inactive内存
f 显示总forks数目
m 显示slabinfo
n 只显示header一次,而不是周期性刷新
s 以表格形式显示各事件统计信息
d 磁盘统计信息
D 磁盘统计信息概要
p partition 分区统计信息
S unit 指定单位,k/K,m/M

mpstat

mpstat [-A] [-u] [-V] [-I {SUM|CPU|SCPU|ALL}] [-P {cpu [,...]|ON|ALL}] [interval[count]]
-A
等价于-u -I ALL -P ALL
-I {SUM|CPU|SCPU|ALL}
中断统计信息
-P {cpu [,…]|ON|ALL}
指定要报告的CPU

其它工具

find

find命令可以用-type指定文件类型:

b Block special file
c Character special file
d Directory
f Plain file
p Named Pipe File
l Symbolic link
s Socket

可以用-size指定文件大小:

27 固定大小
+10000c 大于10000字节
-10000c 小于10000字节

可以用-mtime指定修改时间,用-atime指定访问时间:

7 7天前修改
+7 7天之前修改
-7 7天以内修改

可以用-perm指定文件权限:

664 文件访问权限为664
-020 对020取按位与操作,不为0即所查找

另外可以用-user-group用户和组,后面可以是名字也可以是编号。

find . -print                           # print all files in curdir
ls -ld `find . -print`                  # print all files long list
find . -name "*.c" -o -name "*.cpp"     # find c/c++ files

正确含有空格的文件名

ref: How do I use find when the filename contains spaces?

一种方法是使用-print0选项:

find . -type f -print0 | xargs -0 echo
find . -type f -print0 | xargs -0 -n 1 echo

另一种方式是使用-exec选项:

find . -type f -exec echo '{}' +
find . -type f -exec echo '{}' \;

远程登录

ssh

如果登录出现如下警告,需要更新known_hosts

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
6e:45:f9:a8:af:38:3d:a1:a5:c7:76:1d:02:f8:77:00.
Please contact your system administrator.
Add correct host key in /home/XX/.ssh/known_hosts to get rid of this message.
Offending RSA key in /var/lib/sss/pubconf/known_hosts:4
RSA host key for pong has changed and you have requested strict checking.
Host key verification failed.
ssh-keygen -R hostname
ssh-keygen -R [23.34.56.76]:26          # 指定端口的格式

可以利用配置/etc/ssh/ssh_config设置ssh行为。

StrictHostKeyChecking no                # 避免询问加入known_host