Shell 文件的格式化与相关处理 printf、awk

格式化打印printf

使用场景:将数据格式化输出

语法:printf ‘打印格式’ 实际内容

选项与参数

关于格式方面的几个特殊样式
\n 换行符
\t 水平[tab]按键
\b 后退

%-ns -左对齐,没有则右对齐,输出宽度为n的字符,任何字符都会被显示在 10 个字符宽的字符内,如果不足则自动以空格填充,超过会将内容全部显示出来
%[格式修饰符]nd 整型输出
%ni i代表integer,多少位整数
%c 输出一个字符
%N.nf N代表共输出 N 位数,其中n位小数的

说明

1.printf不是管道命令
2.打印格式外面是单双引号都可以
3.格式只指定了一个参数,但多出来的参数仍然会按照该格式输出,格式化字符串会被重用

printf "%s\n" python shell
python
shell

4.如果没有参数,会用默认值代替,那么%s用null代替,%d用0代替

格式修饰符

修饰符说明案例
将千位分组分隔符应用于输出的整数部分printf “%'d” 12345567890
12,345,567,890
-左对齐printf ‘%5s\n’ 123
空格空格123
printf ‘%-5s’ 123
123
+显示符号printf ‘%+d\n’ 11
+11
空格有符号值为正,显示空格,值为负显示-,+标志会覆盖空格标志printf ‘% d’ -11
-11
printf ‘% d\n’ 3
空格3
#数字格式选择符,进制输出下面说明
0用0填充数字,而不是空格printf ‘%05d\n’ 3
00003
.限制输出字符串的长度,默认长度超过设定值,字符串会全部显示出来。
.n当打印的文本较长时会截断文本留下n个字符,如果“.”后没有指定宽度,默认为0,则不会打印文本。
如果“.”前也指定了宽度,则会在截断文本后补足到指定的宽度。
printf “%4.2s\n” abcdc
空格空格ab
*宽度在字符串或数字之前通过参数指定,可以动态的指定打印文本的宽度。echo 5 “test”

数字格式选择

格式说明案例
%#o八进制数总是以0开头printf “%o” 123
173 输入的十进制123转换为八进制173
printf “%#o” 123
0173
%#x、%#X十六进制数总是以0x/0X开头printf “%x\n” 123
7b
printf “%#x\n” 123
0x7b
%#g、%#G印的浮点数后面跟随0,直到满足精度所需的位数为止printf “%#g\n” 123
123.000

案例.格式化输出文件内容

[ranan@hadoop102 ~]$ printf "%10s\t %5s\t %5\t \n" $(cat pringtf.txt)

输出命令echo

Shell中的输出命令有两个printf与echo

区别echoprintf
自动添加换行符×
设置输出格式×

语法:echo [选项] 输出的内容

选项
-n 不要在最后自动换行
-e 进行反斜杠转义,字符串里面出现转义字符将进行转义
image

案例

输出命令执行结果

echo `date`  #注意是反斜杠

输出内容到文件,文件若不存在则会创建一个文件
> 覆盖写
>> 追加写

数据流重定向

echo 123 >> text.txt

echo 输出文件内容
'<'是重定向符,用于改变标准输入的源(从键盘改为文件fileName);<fileName将fileName文件输入到stdin中。
需要输出可以使用cat命令

[ranan@hadoop102 bin]$ echo $(< myhadoop.sh)

image

awk数据处理工具

sed:一整行的处理
awk:一行分成数个字段来处理,提供一个类编程环境来修改和重新组织文件中的数据

awk 语言的最基本功能是在文件或字符串中基于指定规则浏览和抽取信息,完整的 awk 脚本通常用来格式化文本文件中的信息。

gawk程序是Unix中的原始awk程序的GNU版本,有一些扩展

语法格式

awk options '条件类型1{脚本1} 条件类型2{脚本2}..' filename

options

选项描述
-F fs分隔符 指定分隔符
-f file从指定的文件中读取脚本
-v var=value指定一个变量值,这个会最先执行

注意点

  1. 如果没有指定读取文件,awk也可以从STDIN接受数据

    awk可以处理后续接的文件,也可以读取来自前一个命令的标准输出。
    image
    EOF:End-of-File

  2. 这里的引号只能是单引号

  3. 一个脚本里面多个命令,使用分号隔开

处理流程

1.读入第一行,并将第一行的数据写入$0,$1,$2等变量当中
2.根据条件,判断是否需要进行后续的操作
3.完成所有操作和判断
4.重复1~3直到所有数据处理完。

awk是以行为一次处理的单位,字段为最小的处理单位

AWK内置变量

类型变量名描述
$n当前记录的第n个字段,比如n为1表示第一个字段,n为2表示第二个字段
$0表示执行过程中当前行的文本内容
FILENAME当前输入文件的名。
NR目前awk所处理的第几行数据,如果输入两个文件,第二个文件开始行数会接着上一个文件
FNR当前数据文件中的数据行数,如果输入两个文件,第二个文件开始行数会置0
NF(Field)每一行($0)拥有的字段总数

N F 如 果 有 5 列 , N F = 5 , NF 如果有5列,NF=5, NF5NF=5,NF表示$5,也就是最后一个Field(列)的数据

关系表达式

一般使用关系表达式作为条件。

条件类型条 件说 明
awk保留字BEGIN在 awk 程序一开始,尚未读取任何数据之前执行。BEGIN 后的动作只在程序开始时执行一次
awk保留字END在 awk 程序处理完所有数据,即将结束时执行。END 后的动作只在程序结束时执行一次
关系运算符>大于
关系运算符<小于
关系运算符>=大于等于
关系运算符<=小于等于
关系运算符==等于
关系运算符!=不等于
关系运算符A~B判断字符串 A 中是否包含能匹配 B 表达式的子字符串
关系运算符A!~B判断字符串 A 中是否不包含能匹配 B 表达式的子字符串
正则表达式/正则/BRE和ERE都支持
字段和记录分隔符变量

FS|目前的列的分隔符,默认是空白字符(空格或制表符)
OFS|输出字段的分隔符
FIELDWIDTHS|由空格分隔一系列的数字,FIELDWIDTHS指定每个字段的长度
RS|输入记录分隔符,默认是\n,如RS=" " 表示空白行当作记录分隔符
ORS|输出记录分隔符

案例1:改变输出间隔符

这里我尝试改变FS,以:为分隔符,输出第一个和第二个元素(逗号分隔开),结果这里不太行

[ranan@hadoop102 ~]$ echo $PATH | awk '{print $0}'
/home/ranan/.local/bin:/home/ranan/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/opt/module/jdk1.8.0_212/bin:/opt/module/hadoop-3.1.3/bin:/sbin
[ranan@hadoop102 ~]$ echo $PATH | awk '{FS=":"}{print $1,$2}'
/home/ranan/.local/bin:/home/ranan/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/opt/module/jdk1.8.0_212/bin:/opt/module/hadoop-3.1.3/bin:/sbin

print 结尾自动加\n
printf 结尾不加\n

原因是awk执行的流程是
1.读入第一行,并将第一行的数据写入$0,$1,$2等变量当中
2.根据条件,判断是否需要进行后续的操作
3.完成所有操作和判断

意思是改变分隔符之前,第一行使用默认分隔符进行分割了,那么从第二行开始才是以:分割的。

这里可以通过添加条件类型解决

所以我们需要在读入第一行之前,就处理分隔符

[ranan@hadoop102 ~]$ echo $PATH | awk 'BEGIN {FS=":"}{print $1,$2}'
/home/ranan/.local/bin /home/ranan/bin

假设我们需要输出的字段之间以-划分

[ranan@hadoop102 ~]$ echo $PATH | awk 'BEGIN {FS=":";OFS="-"}{print $1,$2}'
/home/ranan/.local/bin-/home/ranan/bin
案例2.FIELDWIDTHS的使用

一旦设定了FIELDWIDTHS,FS会被忽略
按指定的宽度分隔数据,形成字符。

[ranan@MPI0 ~]$ cat data
1005.3247596.32
115-2.349194.00
-5810.1298100.1
ranan@MPI0 ~]$ awk 'BEGIN{FIELDWIDTHS="3 5 2 5"}{print $1,$2,$3,$4}' data
100 5.324 75 96.32
115 -2.34 91 94.00
-58 10.12 98 100.1

自定义变量

1.字母、数字、下划线,不能数字开头。
2.awk变量区分大小写

赋值
1.同Shell一样,直接赋值,var=value
2.在命令上上赋值,这可以在不改变脚本代码的情况下改变脚本的行为

[ranan@MPI0 ~]$ cat data 
data11,data12,data13
data21,data22,data23
data31,data32,data33
[ranan@MPI0 ~]$ cat script 
BEGIN{FS=","}{print $n}  
[ranan@MPI0 ~]$ awk -f script n=2 data  # 打印第二个字段 $2
data12
data22
data32
[ranan@MPI0 ~]$ awk -f script n=3 data # 打印第三个字段 $3
data13
data23
data33

假设BEGIN位置要输出n,需要使用-v,且在命令行上**-v命令行参数必须放在脚本代码之前**。

[ranan@MPI0 ~]$ cat script 
BEGIN{FS=",";print n}{print $n}
[ranan@MPI0 ~]$ awk -f script n=2 data # 这里BEGIN会比赋值先执行
[ranan@MPI0 ~]$ awk -v n=2 -f script  data # 需要加上-v才行
数组

awk语言使用关联数组提供数组功能,索引值可以是任意文本字符串

语法var[index] = element

数组的遍历

for(var in array){
	statements;
}

删除数组中的索引

delete array[index]

匹配操作符~

匹配操作符~的作用

对某字段使用正则表达式匹配
经常用于在数据文件中搜索特定元素。
匹配是否该行含有xxx

!~表示排除正则表达式的匹配

案例

匹配$1以data开头的记录

gawk '$1 ~ /^data/{ print $0}'

结构化命令

next 跳过当前行

awk中next语句使用:在循环逐行匹配,如果遇到next,就会跳过当前行,直接忽略下面语句。而进行下一行匹配。

awk 'NR%2==1{next}{print NR,$0;}' text.txt
if条件判断式

语法

if(表达式){
	动作1
	动作2
	}
else{
	动作3
	}
	
或者if(表达式)动作1;else动作2  # 单行if后面必须加上分号
[ranan@c105 ~]$ echo x | awk '{if(x>3)print "x大于3";else print"x不大于3" }'
x不大于3
循环

动作一句不用加分号
动作多句用分号隔开

while语法

while (条件){
动作
}

for循环语法
for (变量;条件;计数器){
动作
}

continue和break
break 跳出循环
continue 终止当前循环

内置函数
函数描述
int(x)x的整数部分,取靠近零一侧的最接近的整数
rand()比0大比1小的随机浮点数

字符串函数

函数描述注意点
split(s,a[,r])将s用FS字符或正则表达式r分开放到数组中,返回值是字段总数可以对awk的字段再进行划分
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐