Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4263717
  • 博文数量: 776
  • 博客积分: 13014
  • 博客等级: 上将
  • 技术积分: 10391
  • 用 户 组: 普通用户
  • 注册时间: 2010-02-22 17:00
文章分类

全部博文(776)

文章存档

2015年(55)

2014年(43)

2013年(147)

2012年(20)

2011年(82)

2010年(429)

分类:

2010-09-29 15:58:00

################
#
#      声明
#
################

此文是学习shell过程的笔记,当做小手册使用,方便查找。此文档更新至 2010.09.19


此文语法点及例程出处:

高级Bash脚本编程指南(ABS)

Shell脚本学习指南

sed与awk[第二版]

Shell十三问-ChinaUnix精华贴

Unix.Shell编程(第三版)

学习 bash (第二版)中文版

LINUX命令、编辑器与SHELL编程

以及大量chinaunix.net的shell版块各帖子





################
#
#    paste
#
################

合并两个文件

-d参数: 文件分隔符

文件1:

cat ip.conf
IP:10.238.70.189
IP:10.238.70.190
IP:10.238.70.129
IP:10.238.70.130

文件2:

cat temp
value=".1.3.6.1.4.1.2011.2.23.34"
value=".1.3.6.1.4.1.2011.2.23.13"
value=".1.3.6.1.4.1.2011.2.50"
value=".1.3.6.1.4.1.2011.2.50"

如何把以上两段连接成:

IP:10.238.70.189  value=".1.3.6.1.4.1.2011.2.23.34"
IP:10.238.70.190  value=".1.3.6.1.4.1.2011.2.23.13"
IP:10.238.70.129  value=".1.3.6.1.4.1.2011.2.50"
IP:10.238.70.130  value=".1.3.6.1.4.1.2011.2.50"

paste ip.conf temp  -d " "



################
#
#   expect
#
################



   export命令, 将会使得被export的变量在所运行脚本(或shell)的所有子进程中都可用. 不幸的是, 没有办法将变量export到父进程中, 这里所指的父进程就是调用这个脚本的脚本或shell. 关于export命令的一个重要的用法就是使用在启动文件中, 启动文件用来初始化和设置环境变量, 这样, 用户进程才能够访问环境变量.


################
#
#   source
#
################

        1. 当在命令行中调用的时候, 这个命令将会执行一个脚本. source(或点)命令通常用于重新执行刚修改的初始化文件,如 .bash_profile 和 .profile 等等。例如,如果在登录后对 .bash_profile 中的 EDITER 和 TERM 变量做了修改,则可以用source命令重新执行 .bash_profile 中的命令而不用注销并重新登录。
        
        2. 当在脚本中调用的时候, source file-name 将会加载file-name文件. sourc一个文件(或点命令)将会在脚本中引入代码, 并将这些代码附加到脚本中(与C语言中的#include指令效果相同). 最终的结果就像是在使用"source"的行上插入了相应文件的内容. 在多个脚本需要引用相同的数据, 或者需要使用函数库的情况下, 这个命令非常有用.


################
#
#   启动文件
#
################

    # 这些文件包含别名和环境变量, 正是这些别名和环境变量才使得Bash可以作为一个用户shell来运行, 当系统初始化之后, 这些别名和变量也可被其他的的Bash脚本调用.
   
/etc/profile                       # 系统范围的默认值, 大部分用来设置环境(所有的Bourne类型的shell, 而不仅仅是Bash [1])
   
/etc/bashrc                        # 特定于Bash的, 系统范围函数与别名

$HOME/.bash_profile                # 用户定义的, 环境默认设置, 在每个用户的home目录下都可找到(本地副本保存在/etc/profile)

$HOME/.bashrc                      # 用户定义的Bash初始化文件, 可以在每个用户的home目录下找到(本地副本保存在/etc/bashrc). 只有交互式的shell和用户脚本才会读取这个文件. 请参考Appendix K, 这是一个.bashrc文件的例子.

 
################
#
#   登出文件

#
################

$HOME/.bash_logout            # 用户定义的指令文件, 在每个用户的home目录下找到. 在登出(Bash)shell的时候, 这个文件中的命令就会得到执行.



###################
#
#      echo
#
###################


$'xxx'                 # $'xxx'格式的字符串,具备解释里面标准转义字符的能力. 同 “-e”参数
echo $'aaa\taaa'
aaa    aaa

# 注: 把值分配给变量时,特殊字符失效。
string=$'aaa\nbbb'
echo $string
aaa bbb



-n参数        # 忽略后面的换行符\n, 使之不换行
echo -n "aaa"
aaatest:~/sh#

-e参数        # 打印特殊字符,使之生效。
echo -e 'aaa\nbbb'
aaa
bbb


###################
#
#  Here document
#
###################

cat > file << EOF
1
2
3
EOF

more file
1
2
3

cat << eof
a
b
c
eof
a
b
c


################
#
#   tr 转换字符
#
################

语法:tr [options] source-char-list replace-char-list

转换字符,如大写字符转换为小写字符

1. -s    # 压缩连续重复出现多次的字符
2. -d    # 删除输入流中的字符
3. -c    # 取source-char-list的补集,tr要转换的字符,变成未列在source-char-list中的字符 (处理二进制字节值)
4. -C    # 与-c相似,但处理的是字符 (现行locale所定义的字符)

例:
tr e x cut -d: -f1,6 /etc/passwd |tr : '    '    # 把":"转换为制表符,用单引号
tr -s ':' '\11'                           # 输入中一个或多个连续都冒号在输出中都被替换成一个制表符
tr -s ' ' ' ' < intro                     # 把多个空格替换为单个空格
tr -d ' ' < intro                         # 把空格删除
等同于 sed 's/ //g' intro


##################
#
# 一段密码输入脚本
#
##################

printf "Enter new password:"             # 提示输入
stty -echo                               # 关闭自动打印输入字符功能
read pass < /dev/tty                     # 读取密码
printf "Enter again:"                    # 提示在输入一次
read pass2 < /dev/tty                    # 在读取一次以确定
stty echo                                # 别忘了打开自动打印输入字符功能


#######################
#
#  访问shell脚本的参数
#
#######################

意识原因,当引用脚本参数超过10个时,应用大括号括起
echo first arg is $1
echo tenth arg is ${10}

cat > finduser
#!/bin/bash
who | grep $1

./finduser better


##################
#
#     执行跟踪
#
##################

set -x    打开跟踪
set +x    关闭跟踪

bash -x ./finduser root
+ w
+ grep root
root     pts/0    192.168.5.200    09:23    0.00s  0.04s  0.00s w


#####################
#
#  扩展正规表达式 ERE
#
#####################

ERE有两个元字符(meta字符)
?            匹配0个或一个前置正规表达式
+            匹配1个或多个前置正规表达式

1. 匹配单个字符
例外,在awk里, "\"符号在方括号里有其他含义,如需要匹配左方括号,连字符,由方括号或反斜杠,应用[\[\-\]\\]

2. 后向引用不存在。 \(\)匹配的是字面上的意思

3. 单词匹配(单词定界符)
\<            匹配单词的开头            \ hello
\>            匹配单词的结尾            \>llo => hello


################
#
# cut 提取字段
#
################

# 格式: cut -cchars file

1. -d    # 指定分割符 默认为制表符
2. -f    # 指定提取都第几字段   
3. -c    # 指定提取第几个字符,用逗号隔开,或用-取得字符范围

例:
cut -c5 file         # 提取第五个字符
cut -c1,5,10 file    # 提取第一,五,十的字符
cut -c1-8 file       # 提取第一到第八个字符
cut -c5- file        # 提取第五到行尾都全部字符

cut -d: -f1 /etc/passwd      # 分割符为“:” 提取第一个字段
cut -d: -f1,6 /etc/passwd


################
#
# join 连接字段
#
################

将多个文件结合在一起,每个文件里的每条记录,都共享一个健值(KEY),健值指的是记录中的主字段。

语法:
join [options ...] file1 file2

选项:
-1 field1        从file1取出第一个字段
-2 field2        从file2取出第二个字段

-o file.field        输出file文件中的field字段

-t separator        使用separator作为输入字段分隔字符,而非使用空白。此字符也作为输出的字段分隔字符。

较旧版本里,用: -j1 field1 -j2 field2 来连接字段

例:
结合sales quotas两个文件,以第一个字段为健值

cat sales
# 业务员数据
# 业务员 量
joe            100
jane        200
herman    150
chris        300

cat quotas
# 配额
# 业务员 配额
joe            50
jane        75
herman    80
chris        95

#!/bin/bash

sed '/^#/d'    quotas | sort > quotas.sorted
sed '/^#/d'    sales  | sort > sales.sorted

join quotas.sorted sales.sorted

rm quotas.sorted sales.sorted

结果:
chris 95 300
herman 80 150
jane 75 200
joe 50 100


############
#
# sort 排序
#
############

语法:
sort [ options ] [ file(s) ]

用途:
将输入行按照健值字段与数据类型选项以及locale排序

主要选项:
-b
忽略开头的空白

-c
检查输入是否已正确地排序。如输入未经排序,但退出吗(exit code)为非零值,则不会有任何输出

-d
字典顺序:仅文字数字与空白才有意义

-g
一般数值:以浮点数字类型比较字段。这个选项的运作有点类似-n,差别仅在于这个选项的数字可能有小数点及指数

-f
不区分大小写

-i
忽略无法打印的字符

-k
定义排序健值字段

-m
将已排序的输入文件,合并为一个排序后的输出数据流

-n
以整数类型比较字段

-o outfile
将输出写到指定的文件,而非标准输出。 可指定到输入文件,来改变原始文件

-r
倒置排序,由大变小

-t char
使用单个字符char作为默认的字段分隔字符,取代默认的空白字符

-u
去除重复的记录,只留一条


-k 选项后面接着的是一个字段编号,或一对数字,每个编号后面都可接一个点好的字符位置,或修饰符字母

字段以及字段里的数字由1开始

说明:
1. 如果仅指定一个字段编号,则排序键会自该字段的开始处开始,一直继续到记录的结束(而非字段的结尾)

2. 如一对用逗号隔开的字段数字,则由第一个字段值开始,结束语第二个字段值结尾

3. 使用点号字符位置 -k2.4,5.6指从第二个字段的第四个字符开始,到第五个字段的第六个字符

4. 出现多个-k时,先从第一个健值字段开始排序,找出匹配该健值的记录后,在进行第二个健值字段的排序,以此类推

例:
sort -t: -k1,1 /etc/passwd

sort -t: -k3nr /etc/passwd
更精确为-k3nr,3 或 -k3,3nr 或 -k3,3 -n -r 由于sort在遇到第一个非阿拉伯数字处停止收集数据,所以-k3nr也正确

sort -t: -k4n -k3n /etc/passwd

sort -t: -k4n -u /etc/passwd



#  按照升序排列

1. -u    # 去除重复的行
2. -r    # 反序排列,由大到小
3. -o    # 结果直接写入文件
4. -n    # 按照第一列值排序
   +1n   # 跳过第一列值排序,按照第二列值排序
5. -t    #
6. -k    # 指定第几字段为键值
例:
sort +2n -t: /etc/passwd


############
#
#   uniq
#
############

sort -u 是删除健值重复的记录,并不是记录完全重复的行,要完成后者,需要uniq工具

用途:    删除重复

参数:
1. -d    # 只显示重复的行
2. -u    # 只显示未重复的行
3. -c    # 计数行出现次数

例:
sort /etc/passwd |uniq -d              # 寻找/etc/passwd中都重复行

sort /etc/passwd |cut -f1 -d: |uniq -d # 分割符为“:”按照第一段,查找重复行


##########################
#
#  wc 计算行数 字数 字符数
#
##########################

默认输出是一样报告,包括 行数 字数 字符数

参数:
-c            只显示字节数
-l            只显示行数
-w            只显示字数

可也接受命令行参数

wc /etc/passwd


################################
#
#  head / tail 提取开头或结尾数行
#
################################

显示文件前n行,方法如下:

head -n n file(s)            head -n 3 urfile

head -n file(s)              head -3 urfile

awk 'FNR <= n' file(s)

sed nq file    (实验中,无法处理多个文件)

sed -n '1,3p'    file (实验中,无法处理多个文件)

显示文件后n行:

tail -n n file

tail -n file

可持续监测一个文件

tail -f file


#####################
#
#   变量赋值与环境
#
#####################


readonly name[=word]            变量值不可更改

export name[=word]

参数:
-p            打印变量

export PATH=$PATH:/usr/local/bin

unset            删除变量或函数
-v               删除变量
-f               删除函数

unset -f 删除函数


################
#
#  $ 位置参数
#
################

$#    # 位置参数的个数
$*    # 把所有传入的参数当成一个参数 (在没有双引号""的情况下,$*与$@是一样的)
$@    # 分着处理所有传入的参数
$$    # 当前shell的PID值
$?    # 上一条命令的推出状态值
$!    # 最近一个进入后台作业的PID
$0    # shell程序的名称
$-    # 表示当前已打开的shell选项

补充:{}标记法提供了一种提取从命令行传递到脚本的最后一个位置参数的简单办法. 但是这种方法同时还需要使用间接引用.

1 args=$#            # 位置参数的个数.
2 lastarg=${!args}
3 # 或:       lastarg=${!#}
4 #           (感谢, Chris Monson.)
5 # 注意, 不能直接使用 lastarg=${!$#} , 这会产生错误.

\c    # 换行
\b    # 退格
\f    # 换页
\n    # 换行
\r    # 回车
\t    # 制表符 tab
\v    # 纵向制表
-n    # 删除尾行的换行符
\042  # “
\047  # '
\033  # esc
\137  # _


#################
#
#       正规
#
#################

POSIX字符类通常都要用引号或双中括号([[ ]])引起来.

[:alnum:]    # 匹配字母和数字. 等价于A-Za-z0-9.
[:alpha:]    # 匹配字母. 等价于A-Za-z.
[:blank:]    # 匹配一个空格或是一个制表符(tab).
[:cntrl:]    # 控制字符
[:digit:]    # 匹配(十进制)数字. 等价于0-9.
[:graph:]    # 非空格字符
[:lower:]    # 匹配小写字母. 等价于a-z.
[:print:]    # 可显示字符
[:punct:]    # 标点符号字符
[:space:]    # 匹配空白字符(空格和水平制表符).
[:upper:]    # 匹配大写字母. 等价于A-Z.
[:xdigit:]   # 匹配16进制数字. 等价于0-9A-Fa-f.


####################
#
#   test 比较 测试
#
####################

$((...))    对里面的算术表达式进行计算



判断字符串
test -n 字符串        # 字符串的长度非零
test -z 字符串        # 字符串的长度为零

[  ]            条件测试. 条件测试表达式放在[ ]中

[[  ]]        扩展,可包含逻辑符,如:&&, ||, <, >
                    在[[和]]之间所有的字符都不会发生文件名扩展或者单词分割, 但是会发生参数扩展和命令替换

(( ))            扩展,计算一个算数表达式的结果
                    如表达式结果为 0 ,返回退出码为 1 ,false.
                    如表达式结果为 非0, 返回退出为 0 ,true.


######################
#
#  test中的 文件 比较
#
######################

if [ -f "$file"]
if [ ! -f "$file"] 测试否定的结果

-e    # 文件存在
-b    # 块设备文件
-c    # 字符设备文件
-d    # 目录
-f    # 一般文件
-g    # 有设置它的setgid位
-u    # 有设置它的setuid位
-h    # 符号连接
-L    # 符号连接 (等同于 -h)
-n    # 非 null
-z    # 为 null
-s    # 不是空的
-p    # 管道 (FIFO文件)
-r    # 可读
-w    # 可写
-x    # 可执行
-S    # socket
-O    # 判断你是否是文件的拥有者
-G    # 文件的group-id是否与你的相同
-N    # 从文件上一次被读取到现在为止, 文件是否被修改过
f1 -nt f2    # 文件f1比文件f2新
f1 -ot f2    # 文件f1比文件f2旧
f1 -ef f2    # 文件f1和文件f2是相同文件的硬链接
!            # "非" -- 反转上边所有测试的结果


########################
#
#  字符串比较中微妙的 X
#
########################

当字符串值为空,或开带有一个减号时,test命令会被混淆,前置一个字母X,增加可移植性。

if [ "X$answer" = "Xyes" ] ...


#####################
#
# test中的 整数 比较
#
#####################

-eq            等于              if [ "$a" -eq "$b" ]
-ne            不等于            if [ "$a" -ne "$b" ]
-gt            大于              if [ "$a" -gt "$b" ]
-ge            大于等于          if [ "$a" -ge "$b" ]
-lt            小于              if [ "$a" -lt "$b" ]
-le            小于等于          if [ "$a" -le "$b" ]


<                小于           (("$a" < "$b"))    
<=            小于等于          (("$a" <= "$b"))
>                大于                (("$a" > "$b"))
>=            大于等于          (("$a" >= "$b"))



#######################
#
# test中的 字符串 比较
#
#######################

=         等于            if [ "$a" = "$b" ]
==        等于            if [ "$a" == "$b" ],与=等价.
!=        不等号          if [ "$a" != "$b" ]
<         小于            按照ASCII字符进行排序    if [[ "$a" < "$b" ]]
                            if [ "$a" \< "$b" ] 注意"<"使用在[ ]结构中的时候需要被转义
>         大于            按照ASCII字符进行排序    if [[ "$a" > "$b" ]]
                            if [ "$a" \> "$b" ] 注意">"使用在[ ]结构中的时候需要被转义


####################################
#
#   test中的 NOT AND OR 逻辑 运算符
#
####################################

它们是test运算符
-a            与 AND                if [ ... -a ... ]
-o            或 OR                 if [ ... -o ... ]

例:-a -o 与 && || 的差异
if [ -n "$str" -a -f "$file" ]        正确 一个test命令,两种条件
if [ -n "$str" ] && [ -f "$file" ]    正确 两个命令,以快捷方式计算
if [ -n "$str" && -f "$file" ]        错误 &&为shell的运算符,不可用在test[]里

它们是shell运算符
&&            与
||            或

# NOT
if ! grep pattern myfile > /dev/null
then
        :
else
        ...
fi
: 什么也不做,表示真

# AND (&&)
if grep pattern1 myfile && grep pattern2 myfile
then
        ...
fi

# OR (||)
if grep pattern1 myfile || grep pattern2 myfile
then
        ...
fi


####################
#
#   exit 推出状态值
#
####################

以管理来说,推出状态 0 表示为 成功

例:如果myfile含有模式pattern,则grep的退出状态为0(成功).如果无任何的行匹配此模式,则退出状态为1(失败)
if grep pattern myfile > /dev/null
then
        ...
else
        ...
fi


POSIX的结束状态
0             成功退出
>0            失败
1-125        命令不成功地退出。特定的退出值的含义,是由各个单独的命令定义的
126          命令找到了,但文件无法执行
127          命令找不到
>128         命令因受到信号而死亡


################
#
#  替换运算符
#
################

$(varname:-work)    # varname存在且非null,反其值。否则,反word
            # 用途:如果变量未定义,则返回默认值。
            # 范例:如未定义,则$(count:-0)值为0

$(varname:=word)    # 存在且非null,反其值。否则,设置它为word,并反值。
            # 用途:如果变量未定义,则设置变量为默认值。
            # 范例: 如未定义,则$(conut:=0)设置conut为0

$(varname:?message)    # 存在且非null,反其值。否则,显示varname:message。并退出。
            # 用途:为了捕捉由于变量未定义所导致的错误。
            # 范例:$(count:?"undefined!"),讲显示 count: undefined!,并退出。

$(varname:+word)    # 存在且非null,返回word。否则,返回null。
            # 用途:为测试变量的存在。
            # 范例:如count已定义,则$(count:+1)返回1(即为真)


补充:
${parameter-default}  -- 如果变量parameter没被声明, 那么就使用默认值.

${parameter:-default} -- 如果变量parameter没被设置, 那么就使用默认值.

 31 variable=
 32 # 变量已经被声明, 但是设为空值.
 33
 34 echo "${variable-0}"    # (没有输出)
 35 echo "${variable:-1}"   # 1
 36 #               ^
 37
 38 unset variable
 39
 40 echo "${variable-2}"    # 2
 41 echo "${variable:-3}"   # 3



###################
#
#  模式匹配运算符
#
###################

# 以此为例  PATH /home/tostoy/mem/log.file.name

$(variable#pattern)    # 如模式匹配于变量值的开头处,则删除匹配最短部分,返回剩下值。
            # 范例:$(path#/*/)    结果:/tolstoy/mem/log.file.name
           
$(variable##pattern)    # 如模式匹配于变量值的开头处,则删除匹配的最长部分,返回剩下值。
            # 范例:$(path##/*/)    结果:log.file.name

$(variable%pattern)    # 如模式匹配于变量值的结尾处,则删除匹配的最短部分,返回剩下值。
            # 范例:$(path%.*)    结果:/home/tolstoy/mem/log.file

$(variable%%pattern)    # 如模式匹配于变量值的结尾处,则删除匹配的最长部分,返回剩下值。
            # 范例:$(path%%.*)    结果:/home/tolstoy/mem/log
           
           
           
           
###################
#
#    变量替换
#
###################

${var:pos}                          # 变量var从位置pos开始扩展(译者注: 也就是pos之前的字符都丢弃).

    
${var:pos:len}                      # 变量var从位置pos开始, 并扩展len个字符. 参考例子 A-14, 这个例子展示了这种操作的一个创造性的用法.

    
${var/Pattern/Replacement}          # 使用Replacement来替换变量var中第一个匹配Pattern的字符串.
                                                                    如果省略Replacement, 那么第一个匹配Pattern的字符串将被替换为空, 也就是被删除了.


${var//Pattern/Replacement}         # 全局替换. 所有在变量var匹配Pattern的字符串, 都会被替换为Replacement.
                                                                    和上边一样, 如果省略Replacement, 那么所有匹配Pattern的字符串, 都将被替换为空, 也就是被删除掉.
   
${var/#Pattern/Replacement}         # 如果变量var的前缀匹配Pattern, 那么就使用Replacement来替换匹配到Pattern的字符串.

   
${var/%Pattern/Replacement}         # 如果变量var的后缀匹配Pattern, 那么就使用Replacement来替换匹配到Pattern的字符串.

   
###################
#
#      eval
#
###################

eval arg1 [arg2] ... [argN]

将表达式中的参数, 或者表达式列表, 组合起来, 然后评价它们(译者注: 通常用来执行). 任何被包含在表达示中的变量都将被扩展. 结果将会被转化到命令中. 如果你想从命令行中或者是从脚本中产生代码, 那么这个命令就非常有用了.
 
例1:

for i in 1 2 3 4 5;do
    value=eval $i
    echo $i
done

#  value=$i 具有相同的效果, 在这里并不是非要使用"eval"不可.
#  一个缺乏特殊含义的变量将被评价为自身 -- 也就是说,
#+ 这个变量除了能够被扩展成自身所表示的字符外, 不能被扩展成任何其他的含义.


例2:

for i in ls df;do
    value=eval $i
    echo $i
done

#  value=$i 在这里就与上边这句有了本质上的区别.
#  "eval" 将会评价命令 "ls" 和 "df" . . .
#  术语 "ls" 和 "df" 就具有特殊含义,
#+ 因为它们被解释成命令,
#+ 而不是字符串本身.




################
#
#     case
#
################

case "$1" in
-f)
        ...
        ;;
-d | --directory)
        ...
        ;;
*)
        echo $1: unknown option >&2
        exit 1
        ;;
esac

说明:
1. case 与 in 之间的测试值,可加"",也可不加
2. 每个测试动作后,用;;结束
3. | 字符,用来分隔模式 表示 or
4. 最后测试动作,可以不加;;


################
#
#     for
#
################

# 格式
for var in word1 word2 ...wordn
do
    command
    command
    ...
done

# 将一组命令循环执行预先确定的遍数
# 列表可选,如省略,for 会遍历整个命令行参数 好像: for i in "$@"

例:
for i in 1 2 3
do
    echo $i
done

for i    # 循环通过命令行参数
do
    case $i in
    -f) ...
            ;;
    ...
    esac
done

for file in $(cat filelist)
do
    command
done

for i in file*.txt
do
    ...
done


################
#
#     while
#
################

if/while/until 测试的是执行某个命令的返回值:

执行成功,返回“0”,条件为真; 执行失败,返回“非0”,条件为假。

不要把返回值理解为“条件的真假”,这根本就是俩码事儿。


# 格式
while command1
do
    command
    ...
done

# 先执行 command1,并检测退出状态,如果为0,就执行do和done之间的命令;
# 然后再次执行command1 并检测其退出状态,如果为0,则再次执行do和done之间的命令;
# 直到command1的退出状态不为0为止。

例:
i=1
while [ "$i" -le 5 ]
do
    echo $i
    i=$((i+1))
done

while [ "$#" -ne 0 ]
do
    echo $1
    shift
done

printf "Enter username:"
read user
while true    # true什么也不做,只是成功地退出,表示无限循环
do
    if who | grep "$user" > /dev/null
    then
        break
    fi
    sleep
done


################
#
#     until
#
################

# 格式
until command1
do
    command
    command
    ...
done

# unit后面的命令退出状态不为0,循环就一直执行下去;
# 一旦退出状态为0,循环结束。

# 例:
printf "Enter username:"
read user
until who | grep "^$user" > /dev/null
do
    sleep 30
done
echo "$user has logged on"


################
#
# break 退出循环
#
################

printf "Enter username: "
read user
while true
do
        if who | grep "^$user" > /dev/null
        then
                break
        fi
        sleep 30
done

说明:
true        什么也不做,知识成功的退出,用于无限循环,但在无限循环内必须放一个退出条件
false       表示失败退出,一般用于until false ...的无限循环中

break与continue可接受可选的数值参数,可分别用来指出要中断或继续多少个被包含的循环(如果循环计数需要的是一个在运行时可被技术的表达式,
可以使用$((...))来操作),举例如下:
while condition1
do
        ...
        while condition2
        do
            ...
            break 2
        done
done

说明;
break 2 跳出两层循环


################
#
#   continue
#
################

提早开始下一个重复的循环操作,也就是在到达循环底部之前跳出本次循环操作,而重新开始循环


################
#
#    shift
#
################

处理命令行参数的时候,第一次向左位移一位。原$1消失,$2取代$1


################
#
#    函数
#
################

# wait_for_user
#
# 语法: wait_for_user user [ sleeptime ]

wait_for_user () {
    until who |grep "$1" > /dev/null
    do
        sleep ${2:-30}
    done
}

调用:
wait_for_user username
wait_for_user username 60

在函数体中,位置参数($1,$2,...$#,$*,以及$@)都是函数的参数。父脚本的参数则临时地没函数参数所掩盖或隐藏。
$0依旧是父脚本的名称。当函数完成时,则恢复原来的命令行参数


###################
#
# 函数中的 return
#
###################

返回有shell函数得到的退出值给调用它的脚本

在shell函数里,return命令的功能与工作方式都与exit相同

answer_the_question () {
    ...
    return 42
}

如未提供参数,则使用默认退出状态,也就是最后一个执行的命令的退出状态。可写为: return $?

注意:在shell函数体里使用exit,会终止整个shell脚本!!!


################
#
#    read
#
################

将信息读入一个或多个shell变量
 
"\"       # "\" 会阻止后面新行的生成,使之不换行。

-r        # 原始读取,不做任何处理。不将行结尾处的反斜杠解释为续行字符。

-s        # 不打印输入,与用 stty -echo 关闭自动打印输入字符功能相同。

-nN       # 只接受N个字符的输入。

-p        # 读入输入之前,打印体术符。 可作为提示输入。

自标准输入读取行后,通过shell字段切割的功能($IFS)进行切分。第一个单词赋值给第一个变量,第二个单子赋值给第二个变量,
以此类推,如果单词多于变量,则所有剩下的单词,全部赋值给最后一个变量。 read遇到文件结尾(end-of-file),会以失败值退出。

如输入行以反斜杠结尾,则read会丢弃反斜杠与换行字符,然后继续读取下一行数据。如果使用-r选项,那么read会以字面意义读取最后的反斜线。

例:
while IFS=: read user pass uid gid fulname homedir shell
do
    ...    处理每个用户的行
done

################
#
#    重定向
#
################

POSIX Shell提供了防止文件意外截断的选项 set -C 命令打开shell所谓的禁止覆盖(noclobber)选项,当它在打开状态下,单纯的 > 重定向遇到目标文件
已存在时,就会失败。 >| 运算符则可令noclobber选项失败。

<< 与 <<-            提供行内输入
  program << delimiter 可以在shell脚本正文提供输入数据
  如果定界符以任何一种形式的引号括起来,shell便不会处理输入的内文。

第二种形式有一个负号结尾,所有开头的制表符(Tab)在传递给程序作为输入之前,都从嵌入文件与结束定界符中删除。(只有开头的制表符会删除,
开头的空格则不会删除)

<>                   打开一个文件作为输入与输出只用


make 1> results 2> errs

make 1> results 2>&1            &1是shell的语法,表示标准输出


################
#
#    exec
#
################

这个shell内建命令将使用一个特定的命令来取代当前进程. 一般的当shell遇到一个命令, 它会forks off一个子进程来真正的运行命令. 使用exec内建命令, shell就不会fork了, 并且命令的执行将会替换掉当前shell. 因此, 在脚本中使用时, 一旦exec所执行的命令执行完毕, 那么它就会强制退出脚本.

以新的程序取代shell,或改变shell本身的I/O设置

exec 2> /tmp/$0.log                    # 重定向shell本身的标准错误输出

exec 3< /some/file                     # 打开新文件描述符3

read name rank serno <$3               # 从该文件读取


################
#
#    printf
#
################

格式:
printf "format" arg1 arg2    # format:格式字符串

例:
printf "This is a number: %d\n" 10
This is a number: 10

转义序列只有在格式字符串中会被特别对待,也就是说,出现在参数字符串里的转义序列不会被解释:
$ printf "a atring, no processing: <%s>\n" "A\nB"
a string, no processing:

当你使用%b格式指示符时,printf会解释参数字符串里的转义序列:
$ printf "a string, with processing: <%b>\n" "A\nB"
a string,with processing: B>

转义序列:
\a            警告字符
\b            后退
\c            抑制(不显示)输出结果中任何结尾的换行字符;任何留在参数里的字符,任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\f            换页
\n            换行
\r            回车
\t            水瓶制表符
\v            垂直制表符
\\            一个字母上的反斜杠字符


\ddd        表示1到3位数八进制值的字符。仅在格式字符串中有效
\0ddd        表示1到3位的八进制值字符

格式指示符:
%b            相对应的参数被视为含有要被处理的转义序列之字符串
%c            显示相对应的第一个字符
%d,%i        十进制整数
%e            浮点格式
%E            浮点格式
%f            浮点格式
%g            %e或%f转换,看哪一个较短,则删除结尾的零
%G            %E或%f转换,看哪一个较短,则删除结尾的零
%o            不带正负号的八进制
%s            字符串
%u            不带政府号的十进制
%x            不带正负号的十六进制值,使用a至f表示10至15
%X            不带政府号的十六进制值,
%%            字面意义 %

%flags width.precision format-secifier
width为数字值,指定字段宽度。字段默认为向右对齐,如向左对齐,必须指定"-"标志
如: "%-20s" 会在一个有20个字符宽度的字段里,输出一个向左对齐的字符串,少于20个字符则字段将以空白对齐

printf "|%10s|\n" hello
|     hello|

printf "|%-10s|\n" hello
|hello     |


################
#
#   命令替换
#
################

两种方法:
1. `...`
2. $(...)


################
#
#    expr
#
################

# 四则运算, 一般用于整数,也可用于字符串。

在新的代码里,可使用 test 或 $((...)) 进行这里的所有运算
i=1
while [ "$i" -le 5 ]
    echo i is $i
    i=`expr $i+1`
done
在新的代码里,使用shell的内建算术替换应该会更好:
i=1
while [ "$1" -le 5]
do
    echo i is $i
    i=$((i+1))
done

expr 10 + 10
expr 30 / 3
expr 30 \* 2
LOOP=`expr $LOOP + 1`


#####################
#
#  subShell 与 代码块
#
#####################

subShell是一群被括在圆括号里的命令,会启用新进程,但不影响原进程的环境。

tar -cf - . | (cd /newdir; tar -xpf -)


代码块(code block)概念上与subShell雷同,但不会建立新进程,用花括号{ }括起来,且对主脚本的状态会造成影响(例如它的当前目录)
必须将结束括号防止在换行字符或分号之后,例如:

cd /some/directory || {
    echo could noe chang to /some/directory! >&2
    echo you lose! >&2
    exit 1
}

两者区别:
结构                        定界符                        认可的位置                                                另外的进程
SubShell            ( )                            行上的任何位置                                        是
代码块                    {    }                            在换行字符,分号或关键字之后            否       

要用subshell还是代码块需要根据个人的喜好而定。主要差异在于代码块会与主脚本共享状态。
这么一来,cd命令会对主脚本造成影响,也会对变量赋值产生影响。特别是代码块里的exit会终止整个脚本。
因此,如果你希望将一组命令括起来执行,而不要影响主脚本,则应该使用subshell。否则,请使用代码块。


#####################
#
#      内建命令
#
#####################

命令查找次序: 1. 先找特殊内建命令  2. 再找Shell函数  3. 接下来是一般的内建命令  4. 最后则为$PATH内所列目录下找到的外部命令

这种查找顺序让定义shell函数以扩展或覆盖一般shell内建命令成为可能。


#####################
#
#   内建命令 command
#
#####################

在查找要执行的命令时,为了要避开shell的包含函数。这允许从函数中访问与内建命令相同的命令的内建版本

-P 当查找命令时,使用$PATH的默认值,保证找到系统的工具。

例子:

# cd -- 改变目录时更新PS1
cd () {
    command cd "$@"
    x=$(PWD)
    PS1="${x##*/}\$"
}


#####################
#
#       set
#
#####################

为了打印当前shell的所有变量名称及其值,为了设置或解除设置shell选项的值(可改变shell行为的方式),以及为了改变位置参数的值。

set -x    打开调试
set +x    关闭调试

set -o    常选项
set +o


#####################
#
#      basename
#
#####################

从完整路径名称中取出文件名称部分的传统工具。
它会阶段第一个参数的开头所有字符,一直到最后一个斜杠(含),在报告剩余字符到标准输出上

例:
$ bashname resolv.conf
resolv.conf

$ basename /etc/resolv.conf
resolv.conf

第二个参数来表示文件名结尾,则basename从它的结果中截断任何匹配的结尾:

$ basename /etc/resolv.conf .conf
resolv

$ basename /etc/resolv.conf .pid
resolv.conf


#####################
#
#  开/关 文件描述符
#
#####################

0            标准输入
1            标准输出
2            错误输出

n<&-
关闭输入文件描述符n.

0<&-, <&-
关闭stdin.

n>&-
关闭输出文件描述符n.

1>&-, >&-
关闭stdout.


#####################
#
#      循环参数
#
#####################

{1..10}

seq 10
seq 10 20      # 10 ~ 20 段
seq 10 2 20    # 增量为2

例子:
for n in {1..10}; do echo $n; done
for n in `seq 10`; do echo $n; done
for n in `seq 10 20`; do echo $n; done
for n in `seq 10 2 20`; do echo $n; done
for n in $(seq 10); do echo $n; done


####################
#
#   赋值 加1
#
####################

let "n=$n+1"

n=`expr $n + 1`

n=$((n+1))

let "n+=1"



####################
#
# 产生随机数 $RANDOM
#
####################

# RANDOM 是 Bash的内部函数,返回一个伪随机数,范围在0 - 32767之间. 它不应该被用来产生密匙.

echo $RANDOM                    # 输出一个范围在0 - 32767之间随机数               

echo $((RANDOM%10))             # 产生10以内的随机数





####################
#
#   得到字符串长度
#
####################

${#string}                        # 方法 1

expr length $string               # 方法 2

expr "$string" : '.*'             # 方法 3


###########################
#
#  匹配字符串开头的子串长度
#
###########################

expr "$string" : '$substring'  # $substring是一个正则表达式.

例:
stringZ=abcABC123ABCabc
#       |------|

echo `expr match "$stringZ" 'abc[A-Z]*.2'`   # 8
echo `expr "$stringZ" : 'abc[A-Z]*.2'`       # 8


###########################
#
#       字符串 索引
#
###########################

expr index $string $substring

# 在字符串$string中所匹配到的$substring 第一次所出现的位置.

例:
stringZ=abcABC123ABCabc
echo `expr index "$stringZ" C12`             # 6
                                             # C 字符的位置.

echo `expr index "$stringZ" 1c`              # 3
# 'c' (in #3 position) matches before '1'.
     
     

####################
#
#      提取子串
#
####################


${string:position}

    # 在$string中从位置$position开始提取子串.

    # 如果$string是"*"或者"@", 那么将会提取从位置$position开始的位置参数.
   
${string:position:length}

    # 在$string中从位置$position开始提取$length长度的子串.

例:
stringZ=abcABC123ABCabc
#       0123456789.....
#       0-based indexing.

echo ${stringZ:0}                            # abcABC123ABCabc
echo ${stringZ:1}                            # bcABC123ABCabc
echo ${stringZ:7}                            # 23ABCabc

echo ${stringZ:7:3}                          # 23A
                                             # 提取子串长度为3.

### 从后面提取字串
 16 echo ${stringZ:-4}                           # abcABC123ABCabc
 17 # 默认是提取整个字符串, 就象${parameter:-default}一样.
 18 # 然而 . . .
 19
 20 echo ${stringZ:(-4)}                         # Cabc
 21 echo ${stringZ: -4}                          # Cabc
 22 # 这样, 它就可以工作了.
 23 # 使用圆括号或者添加一个空格可以"转义"这个位置参数.



    如果$string参数是"*"或"@", 那么将会从$position位置开始提取$length个位置参数, 但是由于可能没有$length个位置参数了, 那么就有几个位置参数就提取几个位置参数.

      1 echo ${*:2}          # 打印出第2个和后边所有的位置参数.
      2 echo ${@:2}          # 同上.
      3
      4 echo ${*:2:3}        # 从第2个开始, 连续打印3个位置参数.



### 在$string中从$position开始提取$length长度的子串.

语法: expr substr $string $position $length

      1 stringZ=abcABC123ABCabc
      2 #       123456789......
      3 #       以1开始计算.
      4
      5 echo `expr substr $stringZ 1 2`              # ab
      6 echo `expr substr $stringZ 4 3`              # ABC

expr match "$string" '\($substring\)'

    从$string的开始位置提取$substring, $substring是正则表达式.
expr "$string" : '\($substring\)'

    从$string的开始位置提取$substring, $substring是正则表达式.

      1 stringZ=abcABC123ABCabc
      2 #       =======       
      3
      4 echo `expr match "$stringZ" '\(.[b-c]*[A-Z]..[0-9]\)'`   # abcABC1
      5 echo `expr "$stringZ" : '\(.[b-c]*[A-Z]..[0-9]\)'`       # abcABC1
      6 echo `expr "$stringZ" : '\(.......\)'`                   # abcABC1
      7 # 上边的每个echo都打印出相同的结果.

expr match "$string" '.*\($substring\)'

    从$string的结尾提取$substring, $substring是正则表达式.
expr "$string" : '.*\($substring\)'

    从$string的结尾提取$substring, $substring是正则表达式.

      1 stringZ=abcABC123ABCabc
      2 #                ======
      3
      4 echo `expr match "$stringZ" '.*\([A-C][A-C][A-C][a-c]*\)'`    # ABCabc
      5 echo `expr "$stringZ" : '.*\(......\)'`                       # ABCabc


################
#
#   子串替换
#
################

# 子串替换

# 1使用$replacement来替换第一个匹配的$substring.

    语法: ${string/substring/replacement}

# 2 使用$replacement来替换所有匹配的$substring.

    语法: ${string//substring/replacement}

例:
stringZ=abcABC123ABCabc

echo ${stringZ/abc/xyz}           # xyzABC123ABCabc
                                  # 使用'xyz'来替换第一个匹配的'abc'.
 
echo ${stringZ//abc/xyz}          # xyzABC123ABCxyz
                                  # 用'xyz'来替换所有匹配的'abc'.

# 3 如果$substring匹配$string的开头部分, 那么就用$replacement来替换$substring.
    语法: ${string/#substring/replacement}

# 4 如果$substring匹配$string的结尾部分, 那么就用$replacement来替换$substring.
    语法:${string/%substring/replacement}

例:   
stringZ=abcABC123ABCabc

echo ${stringZ/#abc/XYZ}          # XYZABC123ABCabc
                                  # 用'XYZ'替换开头的'abc'.

echo ${stringZ/%abc/XYZ}          # abcABC123ABCXYZ
                                  # 用'XYZ'替换结尾的'abc'.


################
#
#     数组
#
################

1. 赋值

arr[0]=hello
arr[1]="some text"
arr[2]=.users/steve/memos


2. 显示值

echo ${arr[0]}
hello
echo ${arry[1]}
some text

echo ${arr[*]}    # 显示全部数组元素值

echo ${#arr[*]}    # 显示全部数组元素个数
3


# 提取尾部的子串
arrayZ=( one two three four five five )

echo ${arrayZ[@]:0}     # one two three four five five
                        # 所有元素.

echo ${arrayZ[@]:1}     # two three four five five
                        # element[0]后边的所有元素.

echo ${arrayZ[@]:1:2}   # two three
                        # 只提取element[0]后边的两个元素.



3. ((...))整数计算

typeset -i data    # 申明一个整数数组

例子:
# 不仅在双括号里引用数组元素时,可以忽略美元符号和花括号。
# 当数组申请为整数时,在外面也可以忽略它们。
#
typeset -i array
array[0]=100
arrar[1]=50
(( array[2] = array[0] + array[1]))
echo ${array[2]}
150
i=1
echo ${array[i]}
50
array[3]=array[0]+array[2]
250

例子:
数组赋值:
n=(a b c)
echo ${n}
a
echo ${n[*]}
a b c
echo ${#n[*]}
3


################
#
#      let
#
################

# 四则运算
# 有返回值 当计算结果为0时,返回值为1,否则为0

let a=5
let a=a+2

# " " 加上双引号,两个字符之间可以用空格,增加可读性
let a=5
let "a = a + 2"


################
#
#    grep
#
################

默认使用 BRE
加 -E 参数,使用 ERE 等同于 egrep
加 -F 参数,等同于 fgrep
三种类型:

grep:                使用POSIX定义的基本正规表达式 BRE
egrep:            使用扩展正规表达式 ERE
fgrep:            快速FAST查询,只可以按字符串查找

grep pattern [file...]

-i 选项在搜索时忽略大小写.

-w 选项用来匹配整个单词.

-l 选项仅列出符合匹配的文件, 而不列出匹配行.

-r (递归) 选项不仅在当前工作目录下搜索匹配, 而且搜索子目录.

-n 选项列出所有匹配行, 并显示行号.
 
-v (或者--invert-match)选项将会显示所有不匹配的行.

-c (--count) 选项将只会显示匹配到的行数的总数,而不会列出具体的匹配.

-E    使用扩展正规表达式,取代egrep

-F    使用固定字符串进行匹配,取代fgrep

-f    从文件读取模式进行匹配

-s    不显示错误信息。通常与-q并用

-e    指定其参数为模式,即使它以减号开头

-q    禁止输出



##################################
######                      ######
######         sed          ######
######                      ######
##################################


############
#
#   语法
#
############

1. [address]command
2. [line-address]command
3. address{
    command1
    command2
    command3
    }
   

############
#
#   参数
#
############

-r            用扩展正规表达式

-n            抑制模式自动打印空间


############
#
#  替换 s
#
############

[address]s/pattern/replacement/flags

标志flags:
n    1-512  模式中都第n次出现
g    全局修改
p    打印模式空间内容
w file    模式空间内容写到file中

replacement中都特殊字符:
&    用正规表达式匹配的内容进行替换
/n    匹配第n个字串
\    转移符


############
#
#  删除 d
#
############

n/d      # 删除第n行
/^$/d    # 删除空行


#############################
#
#  追加(a) 插入(i) 更改(c)
#
#############################

追加:
[line-address]a\
    text

插入:
[line-address]i\
    text

更改:
[address]c\
    text


############
#
#  转换 y
#
############

[address]y/abc/xyz

# 用处最多是大小写转换


############
#
#  打印 p
#
############

# 默认有此参数,除非 -n 抑制打印


###############
#
#  打印行号 =
#
###############

[line-address]=

例:打印具有if语句都 行号 和 行
/if/{
=
p
}


############
#
#  下一步 n
#
############

[address]n


############
#
# 读(r) 写(w)
#
############

[line-address]r file
[address]w file

例:
/^/r company.list


############
#
#  退出 q
#
############

[line-address]q



GNU拓展

#######################
###                 ###
###  GNU sed 拓展    ###
###                 ###
#######################


###############
#
#  大小写转换
#
###############

\U    # 小写 -> 大写

\u    # 头母小写 -> 大写

\L    # 大写 -> 小写

\l    # 头母大写 -> 小写


例子:

ly5066113@ubuntu:~$ echo 'abc' | sed 's/.*/\U&/'
ABC
ly5066113@ubuntu:~$ echo 'abc' | sed 's/.*/\u&/'
Abc
ly5066113@ubuntu:~$ echo 'ABC' | sed 's/.*/\L&/'
abc
ly5066113@ubuntu:~$ echo 'ABC' | sed 's/.*/\l&/'
aBC


#####################
#
#  正则匹配忽略大小写
#
#####################

/REGEXP/I


例子:

ly5066113@ubuntu:~$ seq 10 | sed '0~2d'
1
3
5
7
9
ly5066113@ubuntu:~$ seq 10 | sed '1~2d'
2
4
6
8




#########################
#
#  取出奇数行 或者 偶数行
#
#########################

FIRST~STEP


例子:

ly5066113@ubuntu:~$ seq 10 | sed '0~2d'
1
3
5
7
9
ly5066113@ubuntu:~$ seq 10 | sed '1~2d'
2
4
6
8


##################################
#
#  每5行合并成1行,类似 xargs -n5
#
##################################

例子:


ly5066113@ubuntu:~$ seq 20 | xargs -n5
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
ly5066113@ubuntu:~$ seq 20 | sed ':a;N;s/\n/ /;0~5b;ba'
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20



#################
#
#  \%REGEXP%
#
##################

进行路径匹配时,使用此方法,可以剩去很多转义符\


例子:

16 17 18 19 20
ly5066113@ubuntu:~$ echo 'a/b/c/d/e/f' | sed -n '/a\/b\/c\/d/p'
a/b/c/d/e/f
ly5066113@ubuntu:~$ echo 'a/b/c/d/e/f' | sed -n '\%a/b/c/d%p'
a/b/c/d/e/f



地址 +行数,可以实现类似 grep -A N 的功能




#######################
###                 ###
###  高级 sed 命令   ###
###                 ###
#######################

高级命令分成3个组:
1. 处理多行模式空间 (N D P)
2. 采用保持空间来保存模式空间的内容并使它可用于后续的命令(H h G g x)
3. 编写使用分支和条件指令的脚本来更改控制流(: b t)


################
#
#  多行模式空间
#
################

定位元字符(^ &)分别匹配模式空间的开始处和结尾处

"^"        # 匹配空间中的第一个字条,而不匹配换行符后面的字符。
"$"        # 只匹配模式空间中最后的换行符,二不匹配任何迁入的换行符。   


#################
#
#  追加下一行 N
#
#################

n    输出模式空间的内容,然后读取新的输入行,n不创建多行模式空间。
N    读入下一行到模式空间,并添加到模式空间的现有内容之后。初始内容与新输入内容之前用换行符(\n)分割。


#################
#
#   多行删除 D
#
#################

删除命令(D)是删除命令(d)的多行模式。

d    删除模式空间的内容,并读入新的输入行,从而在脚本的顶端重新使用编辑方式。
D    只删除多行模式空间中直到第一个嵌入的换行符的这部分内容,不导入新的输入行,返回到脚本的顶端,将这些指令应用于模式空间的剩余内容。


#################
#
#   多行打印 P
#
#################

P    输出模式空间的第一部分,直到第一个嵌入的换行符为止,在执行完脚本的最后一个命令后,自动输出模式空间的内容。


#################
#
#   包含那一行
#
#################

(Hold)            h 或 H        将模式空间的内容内容复制或追加到保持空间
(Get)             g 或 G        将保持空间的内容内容复制或追加到模式空间
(Exchange)        x             交换保持空间和模式空间的内容


##################################
#
#  高级的流控制命令 b(分支) t(测试)
#
##################################

b(分支)        无条件的将脚本中的控制转移到包含特殊标签的行,如没有标签,转到脚本结尾处。
t(测试)        测试条件,当条件为真时,转移控制。

标签是任意不多于7个字符的序列,以冒号开始
:mylabel

指定标签
b mylable

例1: 用标签完成 是AA就加上YES,不是AA就加NO

ly5066113@ubuntu:~$ cat urfile
AA
BC
AA
CB
CC
AA
ly5066113@ubuntu:~$ sed '/^AA/s/$/ YES/;t;s/$/ NO/' urfile
AA YES
BC NO
AA YES
CB NO
CC NO
AA YES

解释:如果是AA,执行s/$/ YES/,s命令执行成功,执行t命令,没有标签,跳转到命令的结尾,这样就会跳过后面的s/$/ NO/,如果不是AA,s/$/ YES/不执行,
      则t命令也不执行,只执行后面的s/$/ NO/


使用b命令:

ly5066113@ubuntu:~$ sed '/^AA/ba;s/$/ NO/;b;:a;s/$/ YES/' urfile
AA YES
BC NO
AA YES
CB NO
CC NO
AA YES

解释:如果是AA,执行ba,跳转到标签a处,这样会跳过中间的s/$/ NO/;b,只执行后面的s/$/ YES/
      如果不是AA,ba不执行,执行s/$/ NO/,执行b,没有标签,跳转到命令的结尾,这样将会跳过后面的s/$/ YES/


ly5066113@ubuntu:~$ sed '/^AA/ba;s/$/ NO/;b;:a;s/$/ YES/' urfile
AA YES
BC NO
AA YES
CB NO
CC NO
AA YES





##################################
######                      ######
######         awk          ######
######                      ######
##################################


awk ‘BEGIN{动作};模式{动作};模式{动作}。。。;END{动作}’ file


# 正规的扩展模式 -r

awk -r


###################
#
#  awk程序设计模型
#
###################

1. 在读入任何输入前执行第一个例程 BEGIN
2. 在读入每个输入行时执行第二个例程(主输入循环)
3. 在所有的输入完成后执行第三个例程 END

例:
awk '{ print "hello,word" }' test

awk '{ print }' test

awk 'BEGIN { print "hello,word" }'

awk /^$/'{print "this is a blank line." }'


###################
#
#  判断输入的类型
#
###################

# 根据输入内容 判断类型

vim awksrc
/[0-9]+/    {print "that is an integer"}
/[a-zA-Z]+/    {print "that is an staring"}
/^$/        {print "that is an blank line"}

awk -f awksrc


###################
#
#      字段 $n
#
###################

1. $1 表示第一个字段
2. $2 表示第二个字段
3. $0 表示整个输入记录


###################
#
#    分隔符 -F
#
###################

# 默认分隔符为: 空格 或 制表符/t

-F: 指定分隔符
awk -F"/t" '{print $2}' names    # 指定分隔符为制表符/t
awk -f : -f blocklist.awk names



定义系统变量FS 指定分隔符
BEGIN {FS = ','}
{print $1 "," $6}
FS="/t"

如指定多个分隔符:
awk -F'#|@' '{...}'
awk -F"[#,]" '{...}'
awk 'BEGIN{FS="[#,]"};{...}'


###################
#
#    系统变量
#
###################

FS(field separtor):            输入字段分隔符 (默认为一个空格)
OFS:                           输出字段分隔符

NF:(number field)               字段个数    FS="[,:\t]"
$NF:                            引用最后一个字段的值

RS:(record separtor)            输入记录分隔符 (默认为/n 换行符)
ORS:                            输出记录分隔符

NR:(number record)              记录个数(行号),
FNR:                            有多个输入文件时,每个对应文件的行号

NR与FNR不同的地方就在于NR在给多个文件的行编号的时候不会根据文件重新编号,而是按照读取顺序统一编号。





FS                            字段分隔符(一个空行)
RS                            记录分隔符(一个换行符)
NF                            当前记录中的字段个数
NR                            当前记录的个数
FNR                           和NR类似,但和当前文件相关
OFS                           输出字段分隔符(一个空行)
ORS                           输出记录分隔符(一个换行符)
RLENGTH                       和函数match()匹配的字符串的长度
RSTART                        和函数match()匹配的字符串的第一位置
ARGC                          命令行中的参数个数
ARGV                          包含命令行参数的数组
CONVFMT                       用于数字的字符串转换格式(%.6g)POSIX
ENVIRON                       环境变量的关联数组
FILENAME                      当前文件名
OFMT                          数字的输出格式
SUBSEP                        数组下标的分割字符(\034)


##################
#
#   关系操作符
#
##################

<             小于       
>             大于
<=            小于等于
>=            大于等于
==            等于
!=            不等于
~             匹配
!~            不匹配

awk 'NF==5 {print "有五个字段"}' urfile
awk '$5 ~ /MA/ {print $1 "," $6}' urfile


###################
#
#    布尔操作符
#
###################

布尔操作符可以将一系列的比较组合起来

||            逻辑或
&&            逻辑与
!            逻辑非


###################
#
# 格式化打印 printf
#
###################

完整语法:
printf(format_expression[,arguments])

printf不带自动换行符,需手动指定"\n"
其中圆括号可选
第一部分描述表达式的格式,通常以引号括起来的字符串常量
第二部分是参数列表,它和格式说明相对应
格式说明前面有个百分号 "%"
s        字符串
d        十进制整数

printf("%d\t%s\n", $5, $9)

格式说明:
%-width.precision format-specifier
%-20s

width            宽度值
-                默认靠右对齐,-指定靠左对齐
precision        修饰十进制或浮点数,控制小数点右边的数字位数。


###################
#
# 向脚本传递参数
#
###################

$1    第一个命令行参数
$2    第二个命令行参数

awk -f scriptfile high=100 low=60 urfile

命令行传递参数,不可用于BEGIN,赋值操作指导这个变量,被求值时才进行

-v 在BEGIN前,给变量赋值。
awk -F"\n" -v RS="" '{print}' urfile


###################
#
#   条件语句 if
#
###################

if(expression)
    action1
else
    action2]
   
expression为真(非零或非空),执行action1,否者执行action2

补充:
1. 如操作由多个语句组成,用大括号括起来
if(expression){
    statement1
    statement2
}

2. 大括号和else后面的换行是可选的
if(expression)action1
[else action2]

3. 如果在action1后面加一个分号表示结束,action1后面的换行也是可选的
if(expression) action1;[else action2]

4. 如果在同一行上用分号分隔多个语句,同样需要使用大括号。


###################
#
#   条件操作符 ?:
#
###################

expr ? action1:action2

grade = ( avg >= 65 ) ? "Pass" : "Fail"


###################
#
#   while 循环
#
###################

while(condition)
    action
   
右括号的换行是可选的

当执行语句大于一条时,用大括号括起来   
i=1
while( i<=4 ){
    print $i
    ++i
}

   
###################
#
#    do 循环
#
###################

至少执行一次,如果while判断语句为真,则继续循环执行,如不为真,则不继续循环,执行后面的action2

do
    action1
while(condition)
    action2

do后面的换行是可选的
如action后面使用分号,则换行也是可选的

{
    total = i = 0
    do {
                ++i
                total += $i
            }while(total<=100)
                print i,":",total
}

   
###################
#
#    for 循环
#
###################

for(i=1;i<=NF;i++)
    print $i
   

###################
#
# 循环控制 break
#
###################

推出循环,不在续集执行循环

for(x=1;x<=NF;++x)
    if(y==$x){
        print x,$x
        break
}
print


###################
#
# 循环控制 continue
#
###################

在到达循环底部之前终止当前循环,并从循环的顶部开始一个新的循环

for(x=1;x<=NF;++x)
    if(x==3)
        continue
    print x,$x
}


###################
#
#    算术函数
#
###################


###################
#
#     字符串函数
#
###################

gsub(r,s,t)                        在字符串t中用字符串s替换和正规表达式r匹配的所有的字符串。返回替换的个数。如果没有给出t,默认为$0

index(s,t)                        返回字串t在字符串s中最左边的位置,如果没有指定s,则返回0

length(s)                         返回字符串s的长度,当没给出s时,返回$0的长度

match(s,r)                        如果正规表达式r在s中出现,则返回出现的起始位置;如果在s中没有出现r,则返回0。设置RSTART和RLENGTH的值

split(s,a,sep)                   使用字段分隔符sep将字符串s分解到数组a的元素中,返回元素的个数,如果没有给出sep,则使用FS。数组分隔和字段分隔再用同样的方式

sprintf("fmt" ,expr)             对expr使用printf格式说明

sub(r,s,t)                       在字符串t中用s替换正规表达式r的首次匹配。如果成功则返回1,否则返回0。如果没有给出t,默认为$0

substr(s,p,n)                    返回字符串s中从位置p开始最大长度为n的子串。如果没给出n,返回从p开始到剩余的字符串

tolower(s)                       将字符串s中的所有大写字符转换为小写,并返回新串

toupper(s)                       将字符串s中的小写字符转换为大写,并返回新串
阅读(2348) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~