for 变量名 in 列表; do
循环体
done
执行机制:
依次将列表中的元素赋值给 "变量名"; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽, 循环结束
for 循环
列表生成方式:
(1) 直接给出列表
(2) 整数列表:
- (a) {
- start..end
- }
- (b) $(seq [start [step]] end)
(3) 返回列表的命令
$(COMMAND)
(4) 使用 glob, 如:.sh
(5) 变量引用
[email protected], $
for 特殊格式
双小括号方法, 即 ((...)) 格式, 也可以用于算术运算
双小括号方法也可以使 bash Shell 实现 C 语言风格的变量操作
I=10
((I++))
for 循环的特殊格式:
- for ((控制变量初始化; 条件判断表达式; 控制变量的修正表达式))
- do
循环体
done
控制变量初始化: 仅在运行到循环代码段时执行一次
控制变量的修正表达式: 每轮循环结束会先进行控制变量修正运算, 而后再做条件判断
while 循环
while CONDITION; do
循环体
done
CONDITION: 循环控制条件; 进入循环之前, 先做一次判断; 每一次循环之后会再次做判断; 条件为 "true", 则执行一次循环; 直到条件测试状态为 "false" 终止循环
因此: CONDTION 一般应该有循环控制变量; 而此变量的值会在循环体不断地被修正
进入条件: CONDITION 为 true
退出条件: CONDITION 为 false
until 循环
until CONDITION; do
循环体
done
进入条件: CONDITION 为 false
退出条件: CONDITION 为 true
循环控制语句 continue
用于循环体中
continue [N]: 提前结束第 N 层的本轮循环, 而直接进入下一轮判断; 最内层为第 1 层
- while CONDTIITON1; do
- CMD1
- ...
- if CONDITION2; then
- continue
- fi
- CMDn
- ...
- done
循环控制语句 break
用于循环体中
break [N]: 提前结束第 N 层循环, 最内层为第 1 层
- while CONDTIITON1; do
- CMD1
- ...
- if CONDITION2; then
- break
- fi
- CMDn
- ...
- done
循环控制 shift 命令
shift [n]
用于将参量列表 list 左移指定次数, 缺省为左移一次.
参量列表 list 一旦被移动, 最左端的那个参数就从列表中删除. while 循环遍历位置参量列表时, 常用到 shift
- ./doit.sh a b c d e f g h
- ./shfit.sh a b c d e f g h
示例: doit.sh
- #!/bin/bash
- Name: doit.sh
- Purpose: shift through command line arguments
- Usage: doit.sh [args]
- while [ $# -gt 0 ] # or (( $#> 0 ))
- do
- echo $*
- shift
- done
示例: shift.sh
- #!/bin/bash
- #step through all the positional parameters
- until [ -z "$1" ]
- do
- echo "$1"
- shift
- done
- echo
创建无限循环
while true; do
循环体
- done
- until false; do
循环体
Done
特殊用法
while 循环的特殊用法 (遍历文件的每一行)
while read line; do
循环体
done </PATH/FROM/SOMEFILE
依次读取 / PATH/FROM/SOMEFILE 文件中的每一行, 且将行赋值给变量 line
练习
扫描 / etc/passwd 文件每一行, 如发现 GECOS 字段为空, 则将用户名和单位电话为 62985600 填充至 GECOS 字段, 并提示该用户的 GECOS 信息修改成功
select 循环与菜单
- select variable in list
- do
循环体命令
done
select 循环主要用于创建菜单, 按数字顺序排列的菜单项将显示在标准错误上, 并显示 PS3 提示符, 等待用户输入
用户输入菜单列表中的某个数字, 执行相应的命令
用户输入被保存在内置变量 REPLY 中
select 是个无限循环, 因此要记住用 break 命令退出循环, 或用 exit 命令终止脚本. 也可以按 ctrl+c 退出循环
select 经常和 case 联合使用
与 for 循环类似, 可以省略 in list, 此时使用位置参量
定义函数
函数由两部分组成: 函数名和函数体
help function
语法一:
f_name (){
... 函数体...
}
语法二:
function f_name {
... 函数体...
}
语法三:
function f_name () {
... 函数体...
}
函数使用
函数的定义和使用:
可在交互式环境下定义函数
可将函数放在脚本文件中作为它的一部分
可放在只包含函数的单独文件中
调用: 函数只有被调用才会执行
调用: 给定函数名
函数名出现的地方, 会被自动替换为函数代码
函数的生命周期: 被调用时创建, 返回时终止
函数返回值
函数有两种返回值:
函数的执行结果返回值:
(1) 使用 echo 等命令进行输出
(2) 函数体中调用命令的输出结果
函数的退出状态码:
(1) 默认取决于函数中执行的最后一条命令的退出状态码
(2) 自定义退出状态码, 其格式为:
return 从函数中返回, 用最后状态命令决定返回值
return 0 无错误返回
return 1-255 有错误返回
在脚本中定义及使用函数
函数在使用前必须定义, 因此应将函数定义放在脚本开始部分, 直至 shell 首次发现它后才能使用
调用函数仅使用其函数名即可
示例:
- cat func1
- #!/bin/bash
- func1
- hello()
- {
- echo "Hello there today's date is date +%F"
- }
- echo "now going to the function hello"
- hello
- echo "back from the function"
创建函数文件
函数文件示例:
- cat functions.main
- #!/bin/bash
- #functions.main
- findit()
- {
- if [ $# -lt 1 ] ; then
- echo "Usage:findit file"
- return 1
- fi
- find / -name $1 -print
- }
检查载入函数
使用 set 命令检查函数是否已载入. set 命令将在 shell 中显示所有的载入函数
示例:
- set
- findit=( )
- {
- if [ $# -lt 1 ]; then
- echo "usage :findit file";
- return 1
- fi
- find / -name $1 -print
- }
- ...
行 shell 函数
要执行函数, 简单地键入函数名即可
示例:
- findit groups
- /usr/bin/groups
- /usr/local/backups/groups.bak
删除 shell 函数
现在对函数做一些改动后, 需要先删除函数, 使其对 shell 不可用. 使用 unset 命令完成删除函数
命令格式为:
unset function_name
示例:
unset findit
再键入 set 命令, 函数将不再显示
环境函数
使子进程也可使用
声明: export -f function_name
查看: export -f 或 declare -xf
函数参数
函数可以接受参数:
传递参数给函数: 调用函数时, 在函数名后面以空白分隔给定参数列表即可; 例如 "testfunc arg1 arg2 ..."
在函数体中当中, 可使用 $1, $2, ... 调用这些参数; 还可以使用 [email protected], $*, $# 等特殊变量
函数变量
变量作用域:
环境变量: 当前 shell 和子 shell 有效
本地变量: 只在当前 shell 进程有效, 为执行脚本会启动专用子 shell 进程; 因此, 本地变量的作用范围是当前 shell 脚本程序文件, 包括脚本中的函数
局部变量: 函数的生命周期; 函数结束时变量被自动销毁
注意: 如果函数中有局部变量, 如果其名称同本地变量, 使用局部变量
在函数中定义局部变量的方法
local NAME=VALUE
函数递归示例
函数递归:
函数直接或间接调用自身
注意递归层数
递归实例:
阶乘是基斯顿. 卡曼于 1808 年发明的运算符号, 是数学术语, 一个正整数的阶乘 (factorial) 是所有小于及等于该数的正整数的积, 并且有 0 的阶乘为 1, 自然数 n 的阶乘写作 n!
n!=1*2*3*...*n
阶乘亦可以递归方式定义: 0!=1,n!=(n-1)!*n
- n!=n(n-1)(n-2)...1
- n(n-1)! = n(n-1)(n-2)!
函数递归示例
示例: fact.sh
- #!/bin/bash
- fact() {
- if [ $1 -eq 0 -o $1 -eq 1 ]; then
- echo 1
- else
- echo $[$1*$(fact $[$1-1])]
- fi
- }
- fact $1
- fork***
fork*** 是一种恶意程序, 它的内部是一个不断在 fork 进程的无限循环, 实质是一个简单的递归程序. 由于程序是递归的, 如果没有任何限制, 这会导致这个简单的程序迅速耗尽系统里面的所有资源
函数实现
- :(){
- :|:&
- };:
- bomb() {
- bomb | bomb &
- }; bomb
脚本实现
- cat Bomb.sh
- #!/bin/bash
- ./$0|./$0&
信号捕捉 trap
trap '触发指令' 信号
进程收到系统发出的指定信号后, 将执行自定义指令, 而不会执行原操作
trap '' 信号
忽略信号的操作
trap '-' 信号
恢复原信号的操作
trap -p
列出自定义信号操作
trap finish EXIT
当脚本退出时, 执行 finish 函数
trap 示例
- #!/bin/bash
- trap 'echo"signal:SIGINT"' int
- trap -p
- for((i=0;i<=10;i++))
- do
- sleep 1
- echo $i
- done
- trap '' int
- trap -p
- for((i=11;i<=20;i++))
- do
- sleep 1
- echo $i
- done
- trap '-' int
- trap -p
- for((i=21;i<=30;i++))
- do
- sleep 1
- echo $i
- done
数组
变量: 存储单个元素的内存空间
数组: 存储多个元素的连续的内存空间, 相当于多个变量的集合
数组名和索引
索引: 编号从 0 开始, 属于数值索引
注意: 索引可支持使用自定义的格式, 而不仅是数值格式, 即为关联索引, bash4.0 版本之后开始支持
bash 的数组支持稀疏格式 (索引不连续)
声明数组:
declare -a ARRAY_NAME
declare -A ARRAY_NAME 关联数组
注意: 两者不可相互转换
数组赋值
数组元素的赋值
(1) 一次只赋值一个元素
- ARRAY_NAME[INDEX]=VALUE
- weekdays[0]="Sunday"
- weekdays[4]="Thursday"
(2) 一次赋值全部元素
ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)
(3) 只赋值特定元素
ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
(4) 交互式数组值对赋值
read -a ARRAY
显示所有数组: declare -a
引用数组 br/> 引用数组元素
${ARRAY_NAME[INDEX]}
注意: 省略 [INDEX] 表示引用下标为 0 的元素
引用数组所有元素
- ${
- ARRAY_NAME[*]
- }
- ${
- ARRAY_NAME[@]
- }
数组的长度 (数组中元素的个数)br/>${#ARRAY_NAME[*]}
${#ARRAY_NAME[@]}
删除数组中的某元素: 导致稀疏格式
unset ARRAY[INDEX]
删除整个数组
unset ARRAY
数组数据处理 br/> 引用数组中的元素:
数组切片:
${ARRAY[@]:offset:number}
offset 要跳过的元素个数
number 要取出的元素个数
取偏移量之后的所有元素 ${ARRAY[@]:offset}
向数组中追加元素:
ARRAY[${#ARRAY[*]}]=value
关联数组:
- declare -A ARRAY_NAME
- ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2'...)
注意: 关联数组必须先声明再调用
示例
生成 10 个随机数保存于数组中, 并找出其最大值和最小值
- #!/bin/bash
- declare -i min max
- declare -a nums
- for ((i=0;i<10;i++));do
- nums[$i]=$RANDOM
- [ $i -eq 0 ] && min=${
- nums[$i]
- } && max=${
- nums[$i]
- }&& continue
- [ ${
- nums[$i]
- } -gt $max ] && max=${
- nums[$i]
- }
- [ ${
- nums[$i]
- } -lt $min ] && min=${
- nums[$i]
- }
- done
- echo "All numbers are ${nums[*]}"
- echo Max is $max
- echo Min is $min
示例
? 编写脚本, 定义一个数组, 数组中的元素对应的值是 / var/log 目录下所有以. log 结尾的文件; 统计出其下标为偶数的文件中的行数之和
- #!/bin/bash
- #
- declare -a files
- files=(/var/log/.log)
- declare -i lines=0
- for i in $(seq 0 $[${
- #files[]
- }-1]); do
- if [ $[$i%2] -eq 0 ];then
- let lines+=$(wc -l ${
- files[$i]
- } | cut -d' ' -f1)
- fi
- done
- echo "Lines: $lines."
字符串切片
?${#var}: 返回字符串变量 var 的长度
?${var:offset}: 返回字符串变量 var 中从第 offset 个字符后 (不包括第 offset 个字符) 的字符开始, 到最后的部分, offset 的取值在 0 到 ${#var}-1 之间 (bash4.2 后, 允许为负值)
?${var:offset:number}: 返回字符串变量 var 中从第 offset 个字符后 (不包括第 offset 个字符) 的字符开始, 长度为 number 的部分
?${var: -length}: 取字符串的最右侧几个字符
注意: 冒号后必须有一空白字符
?${var:offset:-length}: 从最左侧跳过 offset 字符, 一直向右取到距离最右侧 lengh 个字符之前的内容
?${var: -length:-offset}: 先从最右侧向左取到 length 个字符开始, 再向右取到距离最右侧 offset 个字符之间的内容
注意:-length 前空格
字符串处理
? 基于模式取子串
${var#Word}: 其中 Word 可以是指定的任意字符
功能: 自左而右, 查找 var 变量所存储的字符串中, 第一次出现的 Word, 删除字符串开头至第一次出现 Word 字符串 (含) 之间的所有字符
${var##Word}: 同上, 贪婪模式, 不同的是, 删除的是字符串开头至最后一次由 Word 指定的字符之间的所有内容
? 示例:
- file="var/log/messages"
- ${
- file#/
- }: log/messages
- ${
- file##/
- }: messages
字符串处理
?${var%Word}: 其中 Word 可以是指定的任意字符
功能: 自右而左, 查找 var 变量所存储的字符串中, 第一次出现的 Word, 删除字符串最后一个字符向左至第一次出现 Word 字符串 (含) 之间的所有字符
- file="/var/log/messages"
- ${
- file%/
- }: /var/log
?${var%%Word}: 同上, 只不过删除字符串最右侧的字符向左至最后一次出现 Word 字符之间的所有字符
? 示例:
- url=http://www.magedu.com:80
- ${
- url##:
- } 80
- ${
- url%%:*
- } http
字符串处理
? 查找替换
${var/pattern/substr}: 查找 var 所表示的字符串中, 第一次被 pattern 所匹配到的字符串, 以 substr 替换之
- ${
- var//pattern/substr
}: 查找 var 所表示的字符串中, 所有能被 pattern 所匹配到的字符串, 以 substr 替换之
- ${
- var/#pattern/substr
}: 查找 var 所表示的字符串中, 行首被 pattern 所匹配到的字符串, 以 substr 替换之
${var/%pattern/substr}: 查找 var 所表示的字符串中, 行尾被 pattern 所匹配到的字符串, 以 substr 替换之
字符串处理
? 查找并删除
${var/pattern}: 删除 var 表示的字符串中第一次被 pattern 匹配到的字符串
${
var//pattern
}: 删除 var 表示的字符串中所有被 pattern 匹配到的字符串
${
var/#pattern
}: 删除 var 表示的字符串中所有以 pattern 为行首匹配到的字符串
${var/%pattern}: 删除 var 所表示的字符串中所有以 pattern 为行尾所匹配到的字符串
? 字符大小写转换
${var^^}: 把 var 中的所有小写字母转换为大写
${var,,}: 把 var 中的所有大写字母转换为小写
变量赋值
高级变量用法 - 有类型变量
?Shell 变量一般是无类型的, 但是 bash Shell 提供了 declare 和 typeset 两个命令用于指定变量的类型, 两个命令是等价的
?declare [选项] 变量名
-r 声明或显示只读变量
-i 将变量定义为整型数
-a 将变量定义为数组
-A 将变量定义为关联数组
-f 显示已定义的所有函数名及其内容
-F 仅显示已定义的所有函数名
-x 声明或显示环境变量和函数
-l 声明变量为小写字母 declare -l var=UPPER
-u 声明变量为大写字母 declare -u var=lower
eval 命令
?eval 命令将会首先扫描命令行进行所有的置换, 然后再执行该命令. 该命令适用于那些一次扫描无法实现其功能的变量. 该命令对变量进行两次扫描
? 示例:
- [[email protected] ~]# CMD=whoami
- [[email protected] ~]# echo $CMD
- whoami
- [[email protected] ~]# eval $CMD
- root
- [[email protected] ~]# n=10
- [[email protected] ~]# echo {
- 0..$n
- }
- {
- 0..10
- }
- [[email protected] ~]# eval echo {
- 0..$n
- }
- 0 1 2 3 4 5 6 7 8 9 10
间接变量引用
? 如果第一个变量的值是第二个变量的名字, 从第一个变量引用第二个变量的值就称为间接变量引用
?variable1 的值是 variable2, 而 variable2 又是变量名, variable2 的值为 value, 间接变量引用是指通过 variable1 获得变量值 value 的行为
- variable1=variable2
- variable2=value
间接变量引用
?bash Shell 提供了两种格式实现间接变量引用
- eval tempvar=\$$variable1
- tempvar=${
- !variable1
- }
? 示例:
- [[email protected] ~]# N=NAME
- [[email protected] ~]# NAME=wangxiaochun
- [[email protected] ~]# N1=${
- !N
- }
- [[email protected] ~]# echo $N1
- wangxiaochun
- [[email protected] ~]# eval N2=\$$N
- [[email protected] ~]# echo $N2
- wangxiaochun
创建临时文件
?mktemp 命令: 创建并显示临时文件, 可避免冲突
- ?mktemp [OPTION]... [TEMPLATE]
- TEMPLATE: filenameXXX
X 至少要出现三个
?OPTION:
-d: 创建临时目录
-p DIR 或 --tmpdir=DIR: 指明临时文件所存放目录位置
? 示例:
- mktemp /tmp/testXXX
- tmpdir=mktemp -d /tmp/testdirXXX
- mktemp --tmpdir=/testdir testXXXXXX
安装复制文件
?install 命令:
install [OPTION]... [-T] SOURCE DEST 单文件
install [OPTION]... SOURCE... DIRECTORY
install [OPTION]... -t DIRECTORY SOURCE...
install [OPTION]... -d DIRECTORY... 创建空目录
? 选项:
-m MODE, 默认 755
- -o OWNER
- -g GROUP
? 示例:
- install -m 700 -o wang -g admins srcfile desfile
- install -m 770 -d /testdir/installdir
expect 命令
?expect 语法:
expect [选项] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]
? 选项
?-c: 从命令行执行 expect 脚本, 默认 expect 是交互地执行的
示例: expect -c 'expect"\n"{send"pressed enter\n"}
?-d: 可以输出输出调试信息
示例: expect -d SSH.exp
?expect 中相关命令
?spawn 启动新的进程
?send 用于向进程发送字符串
?expect 从进程接收字符串
?interact 允许用户交互
?exp_continue 匹配多个字符串在执行动作后加此命令
expect
?expect 最常用的语法 (tcl 语言: 模式 - 动作)
? 单一分支模式语法:
?expect "hi" {send "You said hi\n"}
? 匹配到 hi 后, 会输出 "you said hi", 并换行
? 多分支模式语法:
- expect "hi" {
- send "You said hi\n"
- } \
- "hehe" {
- send "Hehe yourself\n"
- } \
- "bye" {
- send "Good bye\n"
- }
? 匹配 hi,hello,bye 任意字符串时, 执行相应输出. 等同如下:
- expect {
- "hi" {
- send "You said hi\n"
- }
- "hehe" {
- send "Hehe yourself\n"
- }
- "bye" {
- send "Good bye\n"
- }
- }
示例
- #!/usr/bin/expect
- spawn scp /etc/fstab 192.168.8.100:/App
- expect {
- "yes/no" {
- send "yes\n";exp_continue
- }
- "password" {
- send "magedu\n"
- }
- }
- expect eof
示例
- #!/usr/bin/expect
- spawn SSH 192.168.8.100
- expect {
- "yes/no" {
- send "yes\n";exp_continue
- }
- "password" {
- send "magedu\n"
- }
- }
- interact
- #expect eof
示例: 变量
- #!/usr/bin/expect
- set ip 192.168.8.100
- set user root
- set password magedu
- set timeout 10
- spawn SSH [email protected]$ip
- expect {
- "yes/no" {
- send "yes\n";exp_continue
- }
- "password" {
- send "$password\n"
- }
- }
- interact
示例: 位置参数
- #!/usr/bin/expect
- set ip [lindex $argv 0]
- set user [lindex $argv 1]
- set password [lindex $argv 2]
- spawn SSH [email protected]$ip
- expect {
- "yes/no" {
- send "yes\n";exp_continue
- }
- "password" {
- send "$password\n"
- }
- }
- interact
- #./ssh3.exp 192.168.8.100 root magedu
示例: 执行多个命令
- #!/usr/bin/expect
- set ip [lindex $argv 0]
- set user [lindex $argv 1]
- set password [lindex $argv 2]
- set timeout 10
- spawn SSH [email protected]$ip
- expect {
- "yes/no" {
- send "yes\n";exp_continue
- }
- "password" {
- send "$password\n"
- }
- }
- expect "]#" {
- send "useradd haha\n"
- }
- expect "]#" {
- send "echo magedu |passwd --stdin haha\n"
- }
- send "exit\n"
- expect eof
- #./ssh4.exp 192.168.8.100 root magedu
示例: shell 脚本调用 expect
- #!/bin/bash
- ip=$1
- user=$2
- password=$3
- expect <<EOF
- set timeout 20
- spawn SSH [email protected]$ip
- expect {
- "yes/no" {
- send "yes\n";exp_continue
- }
- "password" {
- send "$password\n"
- }
- }
- expect "]#" {
- send "useradd hehe\n"
- }
- expect "]#" {
- send "echo magedu |passwd --stdin hehe\n"
- }
- expect "]#" {
- send "exit\n"
- }
- expect eof
- EOF
- #./ssh5.sh 192.168.8.100 root magedu
- awk
选项:
-F "分隔符" 知名输入时用到的字段分隔符
-v var=value 变量赋值
分隔符, 域和记录
~ awk 执行时, 由分隔符分隔的字段段 (域) 标记 $1,$2...$n 称为域标识.$0 为所有域, 注意: 此时和 shell 中变量 $ 符含义不同
? 文件的每一行称为记录
? 省略 action, 则默认执行 print $0 的操作
awk
print 格式: print item1, item2, ...
要点:
(1) 逗号分隔符
(2) 输出 item 可以字符串, 也可是数值; 当前记录的字段, 变量或 awk 的表达式
(3) 如省略 item, 相当于 print $0
示例:
- awk '{print"hello,awk"}'
- awk -F: '{print}' /etc/passwd
- awk -F: '{print"wang"}' /etc/passwd
- awk -F: '{print $1}' /etc/passwd
- awk -F: '{print $0}' /etc/passwd
- awk -F: '{print $1"\t"$3}' /etc/passwd
- grep "^UUID"/etc/fstab | awk '{print $2,$4}'
awk 变量
变量: 内置和自定义变量
FS: 输入字段分隔符, 默认为空白字符
- awk -v FS=':' '{print $1,FS,$3}' /etc/passwd
- awk -F: '{print $1,$3,$7}' /etc/passwd
OFS: 输出字段分隔符, 默认为空白字符
awk -v FS=':' -v OFS=':' '{print $1,$3,$7}' /etc/passwd
RS: 输入记录分隔符, 指定输入时的换行符
awk -v RS='''{print }' /etc/passwd
ORS: 输出记录分隔符, 输出时用指定符号代替换行符
awk -v RS=''-v ORS='###''{print }' /etc/passwd
NF: 字段数量
awk -F:'{print NF}' /etc/fstab 引用变量时, 变量前不需加 $
awk -F:'{print $(NF-1)}' /etc/passwd
NR: 记录号
- awk '{print NR}' /etc/fstab ; awk END'{print NR}' /etc/fstab
- awk
?FNR: 各文件分别计数, 记录号
awk '{print FNR}' /etc/fstab /etc/inittab
?FILENAME: 当前文件名
awk '{print FILENAME}' /etc/fstab
?ARGC: 命令行参数的个数
- awk '{print ARGC}' /etc/fstab /etc/inittab
- awk 'BEGIN {print ARGC}' /etc/fstab /etc/inittab
?ARGV: 数组, 保存的是命令行所给定的各参数
- awk 'BEGIN {print ARGV[0]}' /etc/fstab /etc/inittab
- awk 'BEGIN {print ARGV[1]}' /etc/fstab /etc/inittab
awk 变量
? 自定义变量 (区分字符大小写)
(1) -v var=value
(2) 在 program 中直接定义
? 示例:
- awk -v test='hello gawk' '{print test}' /etc/fstab
- awk -v test='hello gawk' 'BEGIN{print test}'
- awk 'BEGIN{test="hello,gawk";print test}'
- awk -F:'{sex="male";print $1,sex,age;age=18}' /etc/passwd
- cat awkscript
- {
- print script,$1,$2
- }
- awk -F: -f awkscript script="awk" /etc/passwd
printf 命令
? 格式化输出: printf "FORMAT", item1, item2, ...
(1) 必须指定 FORMAT
(2) 不会自动换行, 需要显式给出换行控制符,\n
(3) FORMAT 中需要分别为后面每个 item 指定格式符
? 格式符: 与 item 一一对应
%c: 显示字符的 ASCII 码
%d, %i: 显示十进制整数
%e, %E: 显示科学计数法数值
%f: 显示为浮点数
%g, %G: 以科学计数法或浮点形式显示数值
%s: 显示字符串
%u: 无符号整数
%%: 显示 % 自身
? 修饰符
#[.#] 第一个数字控制显示的宽度; 第二个 #表示小数点后精度,%3.1f
左对齐 (默认右对齐) %-15s
显示数值的正负符号 %+d
printf 示例
- awk -F: '{printf"%s",$1}' /etc/passwd
- awk -F: '{printf"%s\n",$1}' /etc/passwd
- awk -F: '{printf"%-20s %10d\n",$1,$3}' /etc/passwd
- awk -F:'{printf"Username: %s\n",$1}' /etc/passwd
- awk -F: '{printf"Username: %s,UID:%d\n",$1,$3}' /etc/passwd
- awk -F: '{printf"Username: %15s,UID:%d\n",$1,$3}' /etc/passwd
- awk -F: '{printf"Username: %-15s,UID:%d\n",$1,$3}' /etc/passwd
操作符
? 算术操作符:
x+y, x-y, x*y, x/y, x^y, x%y
x: 转换为负数
+x: 将字符串转换为数值
? 字符串操作符: 没有符号的操作符, 字符串连接
? 赋值操作符:
=, +=, -=, *=, /=, %=, ^=,++, --
? 下面两语句有何不同
- ?awk 'BEGIN{i=0;print ++i,i}'
- ?awk 'BEGIN{i=0;print i++,i}'
操作符
? 比较操作符:
==, !=,>,>=, <, <=
? 模式匹配符:
~: 左边是否和右边匹配, 包含
!~: 是否不匹配
? 示例:
- awk -F: '$0 ~ /root/{print $1}' /etc/passwd
- awk '$0~"^root"' /etc/passwd
- awk '$0 !~ /root/' /etc/passwd
- awk -F: '$3==0' /etc/passwd
操作符
? 逻辑操作符: 与 &&, 或 ||, 非!
? 示例:
- ?awk -F: '$3>=0 && $3<=1000 {print $1}' /etc/passwd
- ?awk -F: '$3==0 || $3>=1000 {print $1}' /etc/passwd
- ?awk -F: '!($3==0) {print $1}' /etc/passwd
- ?awk -F: '!($3>=500) {print $3}' /etc/passwd
? 条件表达式 (三目表达式)
selector?if-true-expression:if-false-expression
? 示例:
- awk -F: '{$3>=1000?usertype="Common User":usertype=" SysUser";printf"%15s:%-s\n",$1,usertype}' /etc/passwd
- awk PATTERN
?PATTERN: 根据 pattern 条件, 过滤匹配的行, 再做处理
(1) 如果未指定: 空模式, 匹配每一行
(2) /regular expression/: 仅处理能够模式匹配到的行, 需要用 // 括起来
- awk '/^UUID/{print $1}' /etc/fstab
- awk '!/^UUID/{print $1}' /etc/fstab
(3) relational expression: 关系表达式, 结果为 "真" 才会被处理
真: 结果为非 0 值, 非空字符串
假: 结果为空字符串或 0 值
? 示例:
- awk -F: 'i=1;j=1{print i,j}' /etc/passwd
- awk '!0' /etc/passwd ; awk '!1' /etc/passwd
- Awk -F: '$3>=1000{print $1,$3}' /etc/passwd
- awk -F: '$3<1000{print $1,$3}' /etc/passwd
- awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd
- awk -F: '$NF ~ /bash$/{print $1,$NF}' /etc/passwd
- awk PATTERN
?4) line ranges: 行范围
startline,endline:/pat1/,/pat2/ 不支持直接给出数字格式
- awk -F: '/^root\>/,/^nobody\>/{print $1}' /etc/passwd
- awk -F: '(NR>=10&&NR<=20){print NR,$1}' /etc/passwd
?(5) BEGIN/END 模式
BEGIN{}: 仅在开始处理文件中的文本之前执行一次
END{}: 仅在文本处理完成之后执行一次
示例
- awk -F : 'BEGIN {print"USER USERID"} {print $1":"$3}
- END{print "END FILE"}' /etc/passwd
- awk -F : '{print"USER USERID";print $1":"$3} END{print"END FILE"}' /etc/passwd
- awk -F: 'BEGIN{print" USER UID \n--------------- "}{print $1,$3}' /etc/passwd
- awk -F: 'BEGIN{print" USER UID \n--------------- "}{print $1,$3}'END{
- print "=============="
- } /etc/passwd
- seq 10 | awk 'i=0'
- seq 10 | awk 'i=1'
- seq 10 | awk 'i=!i'
- seq 10 | awk '{i=!i;print i}'
- seq 10 | awk '!(i=!i)'
- seq 10 |awk -v i=1 'i=!i'
- awk action
? 常用的 action 分类
?(1) Expressions: 算术, 比较表达式等
?(2) Control statements:if, while 等
?(3) Compound statements: 组合语句
?(4) input statements
?(5) output statements:print 等
awk 控制语句
?{ statements;... } 组合语句
- ?if(condition) {
- statements;...
- }
- ?if(condition) {
- statements;...
- } else {
- statements;...
- }
- ?while(conditon) {
- statments;...
- }
- ?do {
- statements;...
- } while(condition)
- ?for(expr1;expr2;expr3) {
- statements;...
- }
- ?break
- ?continue
- ?delete array[index]
- ?delete array
- ?exit
awk 控制语句 if-else
? 语法: if(condition){statement;...}[else statement]
if(condition1){statement1}else if(condition2){statement2}else{statement3}
? 使用场景: 对 awk 取得的整行或某个字段做条件判断
? 示例:
- awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd
- awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
- awk '{if(NF>5) print $0}' /etc/fstab
- awk -F: '{if($3>=1000) {printf"Common user: %s\n",$1} else {printf"root or Sysuser: %s\n",$1}}' /etc/passwd
- awk -F: '{if($3>=1000) printf"Common user: %s\n",$1; else printf"root or Sysuser: %s\n",$1}' /etc/passwd
- df -h|awk -F% '/^\/dev/{print $1}'|awk '$NF>=80{print $1,$5}'
- awk 'BEGIN{ test=100;if(test>90){print"very good"}
- else if(test>60){ print "good"}else{print "no pass"}}'
awk 控制语句
?while 循环
? 语法: while(condition){statement;...}
? 条件 "真", 进入循环; 条件 "假", 退出循环
? 使用场景:
对一行内的多个字段逐一类似处理时使用
对数组中的各元素逐一处理时使用
? 示例:
- awk '/^[[:space:]]linux16/{i=1;while(i<=NF)
- {print $i,length($i); i++}}' /etc/grub2.cfg
- awk '/^[[:space:]]linux16/{i=1;while(i<=NF) {if(length($i)>=10) {print $i,length($i)}; i++}}' /etc/grub2.cfg
awk 控制语句
?do-while 循环
? 语法: do {statement;...}while(condition)
? 意义: 无论真假, 至少执行一次循环体
? 示例:
awk 'BEGIN{ total=0;i=0;do{ total+=i;i++;}while(i<=100);print total}'
awk 控制语句
?for 循环
? 语法: for(expr1;expr2;expr3) {statement;...}
? 常见用法:
- for(variable assignment;condition;iteration process)
- {
- for-body
- }
? 特殊用法: 能够遍历数组中的元素
语法: for(var in array) {for-body}
? 示例:
awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
性能比较
- ?time (awk 'BEGIN{ total=0;for(i=0;i<=10000;i++){total+=i;};print total;}')
- ?time(total=0;for i in {
- 1..10000
- };do total=$(($total+i));done;echo $total)
- ?time(for ((i=0;i<=10000;i++));do let total+=i;done;echo $total)
- ?time(seq -s "+" 10000|bc)
awk 控制语句
?switch 语句
? 语法: switch(expression) {case VALUE1 or /REGEXP/: statement1; case VALUE2 or /REGEXP2/: statement2; ...; default: statementn}
?break 和 continue
- awk 'BEGIN{sum=0;for(i=1;i<=100;i++)
- {if(i%2==0)continue;sum+=i}print sum}'awk'BEGIN{sum=0;for(i=1;i<=100;i++)
- {if(i==66)break;sum+=i}print sum}'
awk 控制语句
- ?break [n]
- ?continue [n]
- ?next:
提前结束对本行处理而直接进入下一行处理 (awk 自身循环)
awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd
awk 数组
? 关联数组: array[index-expression]
?index-expression:
?(1) 可使用任意字符串; 字符串要使用双引号括起来
?(2) 如果某数组元素事先不存在, 在引用时, awk 会自动创建此元素, 并将其值初始化为 "空串"
?(3) 若要判断数组中是否存在某元素, 要使用 "index in array" 格式进行遍历
? 示例:
- weekdays["mon"]="Monday"
- awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";
- print weekdays["mon"]}'awk'!line[$0]++' dupfile
- awk '{!line[$0]++;print $0, line[$0]}' dupfile
awk 数组
? 若要遍历数组中的每个元素, 要使用 for 循环
?for(var in array) {for-body}
? 注意: var 会遍历 array 的每个索引
? 示例: awk'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday"; for(i in weekdays) {print weekdays[i]}}'
- netstat -tan | awk '/^tcp/{state[$NF]++}
- END{for(i in state) { print i,state[i]}}'awk'{ip[$1]++}END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log
awk 函数
? 数值处理:
rand(): 返回 0 和 1 之间一个随机数
awk 'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }'
? 字符串处理:
?length([s]): 返回指定字符串的长度
?sub(r,s,[t]): 对 t 字符串搜索 r 表示模式匹配的内容, 并将第一个匹配内容替换为 s
echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
?gsub(r,s,[t]): 对 t 字符串进行搜索 r 表示的模式匹配的内容, 并全部替换为 s 所表示的内容
echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$0)'
?split(s,array,[r]): 以 r 为分隔符, 切割字符串 s, 并将切割后的结果保存至 array 所表示的数组中, 第一个索引值为 1, 第二个索引值为 2,...
- netstat -tn | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}
- END{for (i in count) {print i,count[i]}}'
awk 函数
? 自定义函数格式:
- function name ( parameter, parameter, ... ) {
- statements
- return expression
- }
? 示例:
- cat fun.awk
- function max(x,y) {
- x>y?var=x:var=y
- return var
- }
- BEGIN{
- a=3;b=2;print max(a,b)
- }
- awk -f fun.awk
awk 中调用 shell 命令
?system 命令
? 空格是 awk 中的字符串连接符, 如果 system 中需要使用 awk 中的变量可以使用空格分隔, 或者说除了 awk 的变量外其他一律用 "" 引用起来
- awk 'BEGIN{system("hostname") }'
- awk 'BEGIN{score=100; system("echo your score is "score) }'
awk 脚本
? 将 awk 程序写成脚本, 直接调用或执行
? 示例:
- cat f1.awk
- {
- if($3>=1000)print $1,$3
- }
- awk -F: -f f1.awk /etc/passwd
- cat f2.awk
- #!/bin/awk -f
- #this is a awk script
- {
- if($3>=1000)print $1,$3
- }
- chmod +x f2.awk
- f2.awk -F: /etc/passwd
向 awk 脚本传递参数
? 格式:
awkfile var=value var2=value2... Inputfile
? 注意: 在 BEGIN 过程中不可用. 直到首行输入完成以后, 变量才可用. 可以通过 - v 参数, 让 awk 在执行 BEGIN 之前得到变量的值. 命令行中每一个指定的变量都需要一个 - v 参数
? 示例:
- cat test.awk
- #!/bin/awk -f
- {
- if($3>=min && $3<=max)print $1,$3
- }
- chmod +x test.awk
- test.awk -F: min=100 max=200 /etc/passwd
来源: http://www.bubuko.com/infodetail-3023846.html