当前位置:  操作系统/服务器>linux

shell脚本学习指南[四](Arnold Robbins & Nelson H.F. Beebe著)

    来源: 互联网  发布时间:2014-10-15

    本文导语:  回忆起一件事情:之前用linux寻找中文输入法的时候,在百度输入了fcitx,然后结果上边有个,您要找的是不是: 讽刺腾讯 。本来一直记不住这个输入法名字,不过以后哥就记住这个输入法的名字是怎么拼了,感谢百度。 第九...

回忆起一件事情:之前用linux寻找中文输入法的时候,在百度输入了fcitx,然后结果上边有个,您要找的是不是: 讽刺腾讯 。本来一直记不住这个输入法名字,不过以后哥就记住这个输入法的名字是怎么拼了,感谢百度。


第九章awk的惊人表现


awk的调用可以定义变量、提供程序并且指定输入文件,语法:

代码如下:

awk [ -F fs ] [ -v var=value ... ] 'program' [ -- ] [ var=value ... ] [file(s) ]
awk [ -F fs ] [ -v var=value ... ] -f programfile [ -- ] [ var=value ... ] [ file(s) ]


短程序通常直接在命令行上提供,而比较长的程序则委托-f选项指定,可以重复使用此选项。如果命令行未指定文件名,则awk会从标准输入读取。 -- 是特殊选项,指出awk本身已经没有更进一步的命令行选项。任何接下来的选项都可被你的程序使用。
-F选项是用来重新定义默认字段分隔字符,且一般惯例将它作为第一个命令选项。紧接-F选项后的fs参数是一个正则表达式或是被提供作为下一个参数。字段分隔字符也可以设置使用内建变量FS所指定。如:
awk -F 't' '{ ... }' files FS="[fv]" files
上边例子-F选项设置的值,应用到第一个文件组,而由FS指定的值,则应用到第二个组。初始化的-v选项必须放在命令行上直接给定的任何程序之前,他们会在程序启动前生效。在一命令行程序之后-v选项会被解释为一个文件名。在命令行上其他地方的初始化会在处理参数时完成,并且会带上文件名,如:
awk '{...}' Pass=1 *.tex Pass=2 *.tex
处理文件的列表两次,第一次Pass设为1,第二次为2。使用字符串值进行初始化无须用引号框起来,除非shell要求这样的引用以保护特殊字符或空白。

特殊文件名-(连字符)表示标准输入。大部分现代的awk实现(不包括POSIX)都认定特殊名称/dev/stdin为标准输入,即使主机操作系统不支持该文件名。同样:/dev/stderr与/dev/stdout可用于awk程序内,分别表示标准错误输出与标准输出。

一般awk命令模式或操作可省略一个,如果模式省略,则每条输入都被操作;如果操作省略,则默认操作为输出匹配模式的记录。虽然模式多半是数字或字符串表达式,不过awk以保留自BEGIN与END提供两种特殊模式。

与BEGIN关联的操作只会执行一次,在任何命令行文件或一般命令行赋值被处理之前,但是在任何开头的-v选项指定已完成之后。它大部分是用来处理程序所需要的任何特殊初始化工作。END操作也是只执行一次。用于所有输出数据已被处理完之后。BEGIN和END模式可以是任意顺序,可以存在awk程序内任何位置。当指定多个BEGIN或END模式,则他们将按照在awk程序里的顺序执行。

awk提供了标量与数组两种变量以保存数据、数字与字符串表达式,还提供了一些语句类型以处理数据:赋值、注释、条件、函数、输入、循环及输出。awk表达式许多功能与c语言相似。awk里注释是从#开始到行尾。跨行语句需要在结尾处加上反斜杠。

awk里的字符串常数是以引号定界,字符串可包含任何8bit的字符除了控制字符NUL以外。因为NUL在底层实现语言(C)里,扮演的是一个字符串中断字符的角色。awk字符串长度视内存而定。反斜杠转义序列允许非打印字符的表示。

awk提供了许多内建函数,可以在字符串上执行,之后再详细说,这会说两个length(string)返回string内的字符数。字符串的比较用的是传统的关系运算符:==、!=、=。比较不同长度的字符串,且其中一个字符串为另一个的初始子字符串时,较短的定义为小于较长的那个。在shell里字符串连接可以直接进行,不需要连接符号。

awk功能强大的地方大多来自于它对正则表达式的支持。有两个运算符:~(匹配)与!~(不匹配)让awk更容易使用正则表达式:"ABC" ~ "^[A-Z]+$"结果为真,正则表达式常量可以用引号或斜杠加以定界:/^[A-Z]+$/。注意如果有字面意义的符号,需要反斜杠来转义。

awk里的数字,都以双精度浮点值表示,如1/32 写成0.03125、3.125e-2等,awk里没有提供字符串转数字的函数,不过想做到也很简单,只要加个零到字符串里,如:s = "123" , n = 0 + s 。这样123便赋值给n了。一般"+123ABC"转化为123,而"ABC123"与""都转化为0。即使awk里所有的数值运算都是在浮点算术内完成,整数值还是可以表示的,只要值不太大,这个值限定在53位,即2^53即9千万亿的样子。awk的数值运算符没有位运算符,多一个指数运算符(^ 或 ** 或 **=,但是避免使用**和*=,它不是POSIX awk的一部分)它是右结合性的,且与赋值运算符是仅有的右结合性运算符。比如a^b^c^d运算顺序是a^(b^(c^d))。awk里的取余运算测试了 5 % 3 是2 ; 5 % -3 是2; -5 % 3 是-2; -5 % -3是-2;发现取余的结果取决于被取余的数的正负。还有一个内建函数:
int(x) 对x取整
rand 取 0到1之间的随机数
srand(x) 设置x为rand的新输入值
cos(x) 给出x的余弦值
sin(x) 给出x的正弦值
atan2(x,y) 给出y/x的正切值
exp(x) 给出e的x次幂
log(x) 给出x的常用对数值(基为e)
sqrt(x) 给出x的正平方根值
exit(x) 结束awk程序,若有x值,则返回x,否则返回0.
index(s,t) 返回t在s中的第一个开始位置,如t不是s的子串,则返回0]
length(x) 求x的长度(字符个数)
substr(s,x,y) 在字符串s中取得从x个字符开始的长度为y的子字符串.

awk内置字符串函数
gsub(r,s) 在整个$0中用s替代r
gsub(r,s,t) 在整个t中用s替代r
index(s,t) 返回s中字符串t的第一位置
length(s) 返回s长度
match(s,r) 测试s是否包含匹配r的字符串
split(s,a,fs) 在fs上将s分成序列a
sprint(fmt,exp) 返回经fmt格式化后的exp
sub(r,s) 用$0中最左边最长的子串代替s
substr(s,p) 返回字符串s中从p开始的后缀部分
substr(s,p,n) 返回字符串s中从p开始长度为n的后缀部分

awk提供许多内建变量,都是大写名称,时常用到的几个有:
FILENAME 当前输入文件的名称
FNR 当前输入文件的记录数
FS 字段分隔字符(正则表达式)(默认为:" ")
NF 当前记录的字段数
NR 在工作中的记录数
OFS 输出字段分隔字符(默认为:" ")
ORS 输出记录分隔字符(默认为:"n")
RS 输入记录分隔字符(仅用于gawk与mawk里的正则表达式)(默认为:"n")

awk允许的测试:
x==y x等于y?
x!=y x不等于y?
x>y x大于y?
x>=y x大于或等于y?
x x >= < showargs.awk
 BEGIN{
       print "ARGC = ",ARGC
       for ( k = 0 ; k < ARGC ; k++)
          print "ARGV[" k "] = [" ARGV[k] "]"
   }

$ awk -v One=1 -v Two=2 -f showargs.awk Three=3 file1 Four=4 file2 file3
ARGC =  6
ARGV[0] = [awk]
ARGV[1] = [Three=3]
ARGV[2] = [file1]
ARGV[3] = [Four=4]
ARGV[4] = [file2]
ARGV[5] = [file3]

正如C/C++中,参数存储在数组项目0、1....、ARGC-1中,第0个项目是awk程序本身的名称。不过与-f 和 -v选项结合性的参数是不可使用的。同样的,任何命令行程序也不可使用:

代码如下:

$ awk 'BEGIN{for(k=0;k 72 { print FILENAME":"FNR":"$0}' file(s)

awk支持语句的连续执行。支持条件语句,if else 类似C语言,支持循环 while(){} 或do{} while()或for( ; ; ){] 类似c语言。还有一个for(key in array) { } 。
如 awk 'BEGIN { for( x=0; x:h2>:g
...
cd top level web site directory
find . -name '*.html' -o -name '*.htm' -type f |
    while read file   
    do
        echo $file  
        mv $file $file.save  
        sed -f $HOME/html2xhtml.sed < $file.save > $file 
    done


书中说了一小节寻找问题文件,意思是文件名里有特殊字符,可以实用find -print0 来解析,但是没搞明白说这些是干嘛用的。

然后介绍了一个执行命令xargs,是为了处理给脚本传参过长的问题,不如有时候我们会写寻找字符串的命令如下:
$ grep POSIX_OPEN_MAX /dev/null $(find /usr/include -type f | sort )
我们在后边一堆文件中寻找 POSIX_OPEN_MAX这样的一个字符串。如果后边find出来的文件很少,那很好,这条命令就会顺利执行,但是如果过长会给出提示:****:Argument list too long. 这样子。我们可以通过getconf ARG_MAX来查看你的系统允许的最大值是多少。上边这条命令有一个文件是空文件/dev/null,这是为了防止find没找到任何文件使grep进入从标准输入获取信息的空等状态,也为了使grep命令有多个文件参数而使结果可以显示文件名和出现的行数。
我们可以解决这样的一个参数过长的问题通过开始提到的xargs命令,如:
$ find /usr/include -type f | xargs grep POSIX_OPEN_MAX /dev/null
这里xargs如果未取得输入文件名,则会默认终止。GNU的xargs支持--null选项:可处理GNU find的-print0选项所产生的NUL结尾的文件名列表。xargs将每个这样的文件名作为一个完整参数,传递给它执行的命令,而没有shell(错误)解释问题或换行符号混淆的危险,然后是交给其后的命令处理它的参数。另外xargs的选项可以控制哪些参数需要被替换,还可以限制传递的参数个数等。

如果了解文件系统的空间信息,我们可以通过find和ls命令配合awk程序协助就可办到,比如:
$ find -ls | awk '{sum +=$7} END {printf("Total: %.0f bytesn",sum)}'
但并不好用,编码长不说还不知道可用空间。有两个好用的命令来解决这一需求:df和du。

df(disk free)提供单行摘要,一行显示一个加载的文件系统的已使用和可实用空间。显示单位具体看相应版本。可以实用-k强制实用kilobytes单位。还有一个选项-l 仅显示本地文件系统,排除网络加载的文件系统。还有-i选项,提供访问inode使用量。GNU的df还提供-h(human-readable)选项,方便阅读。还可以提供一个或多个文件系统名称或加载点来限制输出项目:$ df -lk /dev/sda6 /var 。

du会摘要文件系统的可用空间,但是不会告诉你某个特定的目录树需要多少空间,这是du(disk usage)的工作。不同系统可能有所不同,-k控制单位,-s显示摘要。
GNU版本提供-h,同df。du可以解决的一个常见问题是:找出哪个用户用掉最多的系统空间:$ du -s -k /home/users/* | sort -k1nr | less
假设用户目录全部放在/home/users下。

关于比较文件好用的两个命令cmp和diff。cmp直接后边跟两个文件参数即可,如果不同输出结果会指出第一个不同处的位置,相同没有任何输出。-s可以抑制输出,可以通过$?来查看离开状态码,非零表示不同。diff惯例是将旧文件作为第一个参数,不同的行会以前置左尖括号的方式,对应到左边文件,而前置右尖括号则指的是右边的文件。还有一个扩展是diff3,比较3个文件。

有时候需要修复不同的地方,patch命令提供了十分方便的做法:

代码如下:

$ echo test 1 > test.1
$ echo test 2 > test.2
$ diff -c test.[12] > test.dif
$ patch < test.dif

此时你查看test.1会发现里边的内容已经变为test 2了。patch会尽可能套用不同之处,然后报告失败的部分,由用户自行处理。虽然patch也可以处理一般的diff输出,但是常规都是处理diff -c选项的信息。

如果有你怀疑有很多文件有相同的内容,实用cmp或diff就十分麻烦。这时可以实用file checksum(文件校验和),取得近似线性的性能完成这一繁琐的工作。有很多工具可以提供,如:sum、cksum、checksum,消息摘要工具md5与md5sum,安全性三列(secure-hash)算法工具sha、sha1sum、sha256以及sha384。可惜的是:sum的实例在各个平台都不想同,使得他们的输出无法跨越不同unix版本进行文件校验和的比较。一般的会这样:
$ md5sum /bin/l?
57e35260b8e017f4696b8559ed4f32f2 /bin/ln
0f68a291df1a708e130e0f407a67e9ca /bin/ls
输出结果有32个十六进制数,等同128位,因此两个不同文件最后散列出相同签名的可能性非常低。了解这个后可以写一个简单脚本来实现我们之前的目标了。

代码如下:

#! /bin/sh -
# 根据他们的MD5校验和,显示某种程度上内容机会一直的文件名
#
# 语法:
#       show-indentical-files files

IFS='
    '
PATH=/usr/local/bin:/usr/bin:/bin
export PATH

md5sum "$@" /dev/null 2> /dev/null |
    awk '{
            count[$1]++
            if( count[$1] ==1 ) first[$1]=$0
            if( count[$1] ==2 ) print first[$1]
            if( count[$1] >1 )  print $0
        }' |
    sort | awk '{
            if ( last != $1 ) print ""
            last = $1
            print
            }'

程序很简单,就不弄注释了吧。可以测试一下:
$ show-indentical-files /bin/*
发现好多命令都很能装啊,其实内容都一样的 - -!。

这里说一下数字签名验证,很有用。
软件发布的时候,一般会包含分发文件的校验和,这可以让你方便得知所下载的文件是否与原始文件匹配。不过单独的校验和不能提供验证(verification)工作:如果校验和被记录在你下载软件里的另一个文件中,则攻击者可以恶意的修改软件,然后只需要相应的修改校验和即可。
这个问题的解决方案是公钥加密(public-key cryptography)。在这种机制下,数据的安全保障来自两个相关密钥的存在:一个私密密钥,只有所有者知悉,以及一个公开密钥,任何人都可得知。两个密钥的其中一个用以加密,另一个则用于解密。公开密钥加密的安全性,依赖已知的公开密钥及可被该密钥解密的文本,以提供一条没有实际用途的信息但可被用来回复私密密钥。这一发明最大的突破是解决了一直以来密码学上极为严重的问题:在需要彼此沟通的对象之间,如何安全的交换加密密钥。
私密密钥与公开密钥是如何使用和运作的呢?假设Alice想对一个公开文件签名,她可以使用她的私密密钥(private key)为文件加密。之后Bob再使用Alice的公开密钥(public key)将签名后的文件解密,这么一来即可确信该文件为Alice所签名,而Alice无须泄漏其私密密钥,就能让文件得到信任。
如果Alice想传送一份只有Bob能读的信给他,她应以Bob的公开密钥为信件加密,之后Bob再使用它的私密密钥将信件解密。只要Bob妥善保管其私密密钥,Alice便可确信只有Bob能读取她的信件。
对整个信息加密其实是没有必要的:相对的,如果只有文件的校验和加密,它就等于有数字签名(digital signature)了。如果信息本身是公开的,这种方法便相当有用,不过还需要有方法验证它的真实性。要完整说明公开密钥加密机制,需要整本书才行,可参考《安全性与密码学》。

计算机越来越容易受到攻击,下载文件或软件要很注意安全。一般软件存档文件都并入了文件校验和信息的数字签名,如果不确定下载的东西是否安全,可以验证它。举例:
$ ls -l coreutils-5.0.tar*
-rw-rw-r-- 1 jones devel 6020616 Apr 2 2003 coreutils-5.0.tar.gz
-rw-rw-r-- 1 jones devel 65 Apr 2 2003 coreutils-5.0.tar.gz.sig
$gpg coreutils-5.0.tar.gz.sig #尝试验证此签名
gpg: Signature made Wed Apr 2 14:26:58 2003 MST using DSA key ID D333CBA1
gpg: Can't check signature: public key not found
验证失败,是因为我们还未将签名者的公开密钥加入gpg密钥环。我们可以在签名作者的个人网站找到公开密钥或者通过email询问。然而幸好使用数字签名的人多半会将他们的公开密钥注册到第三方(thrid-party)的公开密钥服务器,且该注册会自动地提供给其他的密钥服务器共享。
将密钥内容存储到临时文件如”temp.key",加到密钥环中:
$ gpg --import temp.key
然后就可以成功验证签名了。

 


    
 
 

您可能感兴趣的文章:

  • Linux C编程一站式学习,高级shell Bash脚本编程指南,
  • Shell脚本学习指南之文本处理工具
  • shell脚本学习指南[一](Arnold Robbins & Nelson H.F. Beebe著)
  • shell脚本学习指南[六](Arnold Robbins & Nelson H.F. Beebe著)
  • Shell脚本学习指南之查找与替换介绍
  • shell脚本学习指南[二](Arnold Robbins & Nelson H.F. Beebe著)
  • shell脚本学习指南[五](Arnold Robbins & Nelson H.F. Beebe著)
  • shell脚本学习指南[三](Arnold Robbins & Nelson H.F. Beebe著)
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • shell脚本如何调用另外一个shell脚本的函数?
  • 急救!关于Shell脚本删除过期文件的问题,Shell脚本达人乱入
  • shell 脚本中命令别名在脚本外无法使用
  • 傻瓜问题,请问shell编程和shell脚本编程的关系
  • C语言调用shell脚本后,通过何种方法能获取脚本中变量的值
  • 想用shell脚本定时执行另一个脚本
  • 一个shell执行另一个带参数shell脚本????????????
  • 如何给shell脚本加密,脚本中有密码。最好是比较直接的,不要说让用 shc
  • Shell脚本调用Sql脚本并向其中传递变量
  • 请问,Shell中如何执行另外一个Shell脚本?
  • 如何传递参数给linux shell 脚本(当脚本从标准输入而不是从文件获取时)
  • nohup执行的shell脚本,全局变量不能传递到脚本中使用吗?
  • cd、zip等命令在shell提示符下能执行,在shell脚本中为什么不能执行呢?
  • shell脚本问题 关于父脚本和子脚本的问题
  • shell脚本错误输出
  • 请教shell脚本启动程序
  • 关于shell脚本的。
  • 关于arm linux下的别名配置脚本如何在进入用户时让shell执行的问题,如bashrc,profile,.bash_profile等脚本,寻求高手解答
  • 高分请教关于Shell脚本执行中断问题?
  • linux shell脚本
  • Centos6下安装Shell下文件上传下载rz,sz命令
  • 不同类型的shell*(K SHELL , C SHELL) 用命令怎么切换?
  • linux bash shell命令:grep文本搜索工具简介
  • 我在执行shell时,想在shell里直接向mysql数据库插入数据,我该如何写shell。
  • Linux下指定运行时加载动态库路径及shell下执行程序默认路径
  • 菜鸟问问题:shell是什么呢?普通的ls、cp、pwd这些命令算不算shell呢?如何把自己写的文件变成shell呢?
  • linux bash shell命令:文本搜索工具grep中用于egrep和 grep -E的元字符扩展集
  • shell变量和子shell的问题请教
  • linux bash shell命令:文本搜索工具Grep命令选项及实例
  • 请问“当前shell”和“子shell”的区别?
  • linux bash shell命令:文本搜索工具grep正则表达式元字符集(基本集)


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3