售前咨询
技术支持
渠道合作

读《Linux Shell脚本攻略》第2章笔记

1. cat (concatenate)

#标准输入和输入文件的内容拼接在一起

echo ‘Text through stdin’ | cat – file.txt

压缩空白行

#将多个空行压缩成单个空格行,如下:

[root@server1 ~]# cat multi_blanks.txt

line1

line2

line3

line4

[root@server1 ~]# cat -s multi_blanks.txt

line1

line2

line3

line4

#用tr移除空白行

[root@server1 ~]# cat multi_blanks.txt | tr -s ‘\n’

line1

line2

line3

line4

制表符显示为^|

单从视觉上很难将制表符同连续的空格区分开。而在用Python之类的语言编写程序时,将将制表符和空格用于代码缩进,具有特殊含义,并进行区别对待。因此,若在应该使用空格的地方误用了制表符的话,就会产生缩进错误。仅仅在文本编辑器中进行查看是很难发现这种错误的。cat有这个特性,可以将制表符重点标记出来。该特性对排除缩进错误非常有用。用cat命令的-T选项能够将制表符标记成^|。例如:

[root@server1 test]# cat file.py

def function();

var = 5

next = 6

third = 7

[root@server1 test]# cat -T file.py

def function();

^Ivar = 5

^I^Inext = 6

^Ithird = 7

2. 录制与回放终端会话

[root@server1 test]# script -t 2> timing.log -a output.session

Script started, file is output.session

[root@server1 test]# ls

2  a1  a2  a3  file.py  ifs.sh  jd2.sh  jd.sh  multi_blanks.txt  output.session  set ff=unix  timing.log

[root@server1 test]# exit

exit

Script done, file is output.session

两个文件被当做script命令的参数。其中一个文件timing.log用于存储时序信息,描述每一个命令在何时运行;另一个文件output.session用于存储命令输出。-t选项用于将时序数据导入stderr。2>则用于将stderr重定向到timing.log。

借助这两个文件:timing.log(存储时序信息)和output.session(存储命令输入信息),我们可以按照下面的方法回放命令执行过程:

[root@server1 test]# scriptreplay timing.log output.session  #按播放命令序列输出

script命令同样可以用于建立可在多个用户直接进行广播的视频会话。

打开两个终端,Terminal1和Terminal2

1)在Terminal1中输入以下命令:

[root@server1 test]# mkfifo scriptfifo

2)在Terminal2中输入以下命令:

[root@server1 test]# cat scriptfifo

3)在Terminal1中输入以下命令:

[root@server1 test]# script -f scriptfifo

Script started, file is scriptfifo

[root@server1 test]# commands

Terminal1就成为了广播员,而Terminal2则成为了听众,不管你在Terminal1中输入什么内容,它都会在Terminal2或者使用了cat scriptfifo命令的终端都会实时播放.

如果需要结束会话,输入exit并按回车键,退出

3. find

列出当前目录及子目录下所有的文件和文件夹

find bash_path -print

bash_path:查找路径

-print:使用’\n’作为定界符打印每一个匹配的文件名(路径)

-print0: 使用’\0′作为定界符打印每一个匹配的文件名(路径)

-name参数:根据文件名或正则表达式匹配搜索,-iname:忽略字母大小写

如果想匹配多个条件中的一个,可以采用or条件操作:

find . \(-name “*.txt” -o -iname “*.pdf” \) -print

-path:参数可以使用通配符来匹配文件路径或文件。-name总是用给定的文件名进行匹配,-path则将文件路径作为一个整体进行匹配。

-regex:和-path类似,只不过-regex是基于正则表达式来匹配文件路径的。正则表达式是通配符的高级形式。如:[a-z0-9]+@[a-z0-9]+.[a-z0-9]+(Email地址形式,+指明在它之前的字符类中的字符可以出现一次或多次)

-iregex:忽略正则表达式的大小写

!:否定参数

-maxdepth:最大深度  #find命令向下的最大深度限制为1(-maxdepth 1)

-mindepth:最小深度

-maxdepth和-mindepth应该作为find的第3个参数出现。如果作为第4个或之后的参数,就可能会影响到find的效率,因为它不得不进行一些不必要的检查。例如,如果-maxdepth作为第4个参数,-type作为第三个参数,find首先会找出符合-type的所有文件,然后在所有匹配的文件中再找出符合指定深度的那些。但是如果反过来,目录深度作为第三个参数,-type作为第四个参数,那么find就能够在找到所有符合指定深度的文件后,再检查这些文件的类型,这才是最有效的搜索顺序。

时间戳(timestamp)

-atime(访问时间):用户最近一次访问文件的时间。

-mtime(修改时间):文件内容最后一次被修改的时间。

-ctime(变化时间):文件元数据(metadata,例如权限或所有权)最后一次改变的时间。

-atime、-mtime、-ctime可作为find的时间参数。单位是天。这些整数值通常还带有-或+;-表示小于,+表示大于。

-amin(访问时间)、-mmin(修改时间)、-cmin(变化时间),单位分钟。

-newer:指定一个用于比较时间戳的参考文件,然后找出比参数文件更长的修改时间的所有文件。

find命令的时间戳操作出来选项对编写系统备份和很有帮助。

基于文件大小的搜索

find . -type f -size +2k #大于2k的文件

find . -type f -size -2k #小于2k的文件

find . -type f -size 2k  #等于2k的文件

除了k单位,还有其他文件大小单位b(块,512字节)、c(字节)、w(字,2字节)、k(千字节)、M(兆字节)、G(吉字节)

删除匹配的文件

-delete可以用来删除find查找到的匹配文件

例如:删除当前目录下所有的.swp文件:

find . -type f -name “*.swp” -delete

基于文件权限和所有权的匹配

find . -type f -perm 644 -print   #打印当前目录下权限为644的文件

find . -type f -name “*.sh” -perm 755 -user root -print  #找出.sh结尾用户名为root权限为755的文件

-exec

find命令可以借助选项-exec与其他命令进行结合。

find . -type f -user root -name “*.swp” -exec rm -rf {} \;

{}是一个特殊的字符串,与-exec选项结合使用。对于每一个匹配的文件,{}会被替换成相应的文件名。

-exec能够同printf结合来生成有用的输出信息。例如:

# find . -type f -name “*.swp” -exec printf “Text file:%s\n” {} \;

Text file:./1.swp

Text file:./2.swp

Text file:./3.swp

find跳过特定的目录

在搜索目录并执行某些操作的时候,有时为了提高性能。需要跳过一些子目录。例如,程序员会在git所管理的开发源码树中查找特定的文件,源代码层级结构总

是会在每个子目录中包含一个.git目录(.git存储每个目录相关的版本控制信息)。因为与版本控制相关的目录对我们而言并没有什么用处,所以没必要去

搜索这些目录。

# find . \( -name “.git” -prune \) -o \( -type f -print \)

\( -name “.git” -prune \)的作用是用于进行排除,它指明了.git目录应该排除掉,而\( -type f -print \)指明了需要执行的动作。这些动作需要被放置在第二个语句块中

4. xargs

可以处理stdin标准输入并将其转换成特定命令的命令行参数。还可以将单行或多行文本输入转换成其他格式,例如单行变多行或是多行变单行。

将多行输入转换成单行输出

[root@server1 test]# cat example.txt

1 2 3 4 5 6

7    8 9 10

11 12

[root@server1 test]# cat example.txt | xargs

1 2 3 4 5 6 7 8 9 10 11 12

将单行输入转换成多行输出

[root@server1 test]# cat example.txt | xargs | xargs  -n 2

1 2

3 4

5 6

7 8

9 10

11 12

-d选项为输入指定一个定制的定界符

# echo “splitXsplitXsplitXsplit” | xargs -d X

split split split split

可以结合-n参数

# echo “splitXsplitXsplitXsplit” | xargs -d X -n 2

split split

split split

读取stdin,将格式化参数传递给命令

[root@server1 test]# cat args.txt

arg1

arg2

arg3

[root@server1 test]# cat args.txt | xargs -n 1 sh ./cecho.sh

arg1 #

arg2 #

arg3 #

[root@server1 test]# cat args.txt | xargs -n 2 sh ./cecho.sh

arg1 arg2 #

arg3 #

[root@server1 test]# cat args.txt | xargs sh ./cecho.sh

arg1 arg2 arg3 #

在上面的例子中,我们直接为特定的命令(例如cecho.sh)提供命令行参数。这些参数都只源于args.txt文件。

-I指定一个替换字符串,与xargs结合使用时,对于每一个参数,命令都会被执行一次。

[root@server1 test]# cat args.txt | xargs -I {} sh ./cecho.sh -p {} -l

-p arg1 -l #

-p arg2 -l #

-p arg3 -l #

-I {}指定了替换字符串。对于每一个命令参数,字符串{}会被从stdin读取到的参数所替换。使用-I的时候,命令就似乎是在一个循环中执行一样。如果有三个参数,那么命令就会连同{}一起被执行三次,而{}在每一次执行中都会被替换为相应的参数。

结合find使用xargs

# find . -tpye f -name “*.txt” -print | xargs rm -f

这样做很危险。可能会删除不必要的文件。我们没法预测分隔find命令输出结果的定界符是’\n’还是’‘。很多文件名都可能包含空格符,而xargs很可能会误认为它们是定界符(例如,hell
text.txt会被xargs误认为hell和text.txt2个文件),只要我们把find的输出作为xargs的输入,就必须将-print0与find结合使用,以字符null来分隔输出。

用find匹配并列出所有.txt文件,然后用xargs将这些文件删除:

# find . -tpye f -name “*.txt” -print0 | xargs -0 rm -f

这样就可以删除所有.txt文件。xargs -0将\0作为输入定界符。

统计源码目录中所有C程序文件的行数

# find src_path -type f -name “*.c” -print0 | xargs -0 wc -l

结合stdin,巧妙运用while语句和子shell

[root@server1 test]# cat example.txt

1 2 3 4 5 6

7    8 9 10

11 12

[root@server1 test]# cat example.txt | (while read arg; do cat $arg;done) 等同于# cat example.txt | xargs -n 1 | xargs -I {} cat {}

cat: 1: No such file or directory

echo22222222222

cat: 3: No such file or directory

cat: 4: No such file or directory

cat: 5: No such file or directory

cat: 6: No such file or directory

cat: 7: No such file or directory

cat: 8: No such file or directory

cat: 9: No such file or directory

cat: 10: No such file or directory

cat: 11: No such file or directory

cat: 12: No such file or directory

在while循环中,可以将cat $arg替换成任意数量的命令,这样我们就可以对同一个参数执行多项命令。我们也可以借助管道,将输出传递给其他命令。这个技巧能适用于各种问题环境。子shell内部的多个命令可作为一个整体来运行。

5. tr

tr可以对来自标准输入的字符进行替换、删除以及压缩。可以将一组字符变成另一组字符,因而通常也被称为转换(translate)命令

tr只能通过stdin(标准输入),而无法通过命令行参数来接受输入。

tr [options] set1 set2

将来自stdin的输入字符从set1映射到set2,并将其输出写入stdout。set1和set2是字符类或字符集。如果两个字符集的长度不相等,

那么set2会不断重复其最后一个字符,直到长度与set1相同。如果set2的长度大于set1,那么在set2中超过set1长度的那部分字符则全部被忽略。

用tr删除字符

选项-d,可以通过指定需要被删除的字符集合,将出现在stdin中的特定字符清除掉:

[root@server1 test]# echo “Hello 123 world 345″ | tr -d ’0-9′

Hello  world

字符集补集-c

从输入文本中将不再补集中的所有字符全部删除

[root@server1 test]# echo hello 1 char 2 next 4 | tr -d -c ’0-9 \n’

1  2  4

用tr压缩字符

-s选项可以压缩输入中重复的字符:

[root@server1 test]# echo “GNU is     not UNIX. Recursive right ?” | tr -s ‘ ‘

GNU is not UNIX. Recursive right ?

[root@server1 test]# cat sum.txt

1

2

3

4

5

[root@server1 test]# cat sum.txt | echo $[$(tr ‘\n’ ‘+’ ) 0]

15

tr将’\n’替换成’+’,因为我们得到了字符串”1+2+3+4+5+”,但是在字符串的尾部多加了一个操作符+。为了抵消这个多出的操作符,于是追加一个0.

字符类

tr可以像使用集合一样使用各种不同的字符类,如下:

alnum:字母和数字

alpha:字母

cntrl:控制(非打印)字符

digit:数字

graph:图形字符

lower:小写字母

print:可打印字符

punct:标点符号

space:空白字符

upper:大写字母

xdigit:16进制字符

6. 排序、单一与重复

sort、uniq

按数字进行排序

# sort -n file.txt

按逆序进行排序

# sort -r file.txt

按月份进行排序(按照一月、二月、三月……这样的顺序)

sort -M months.txt

根据捡或列进行排序

[root@server1 test]# cat data.txt

1 mac     2000

2 winxp   4000

3 bad     1000

4 linux   1000

-k指定了排序应该按照哪一个键(列号)来进行。

[root@server1 test]# sort -nrk 1 data.txt   # -nr表示按第一列数字逆序排序,

4 linux   1000

3 bad     1000

2 winxp   4000

1 mac     2000

[root@server1 test]# sort -k 2 data.txt

3 bad     1000

4 linux   1000

1 mac     2000

2 winxp   4000

留意用于按照数字顺序进行排序的选项-n。就依据字母表排序和依据数字排序,sort命令对于字母表排序和数字排序有不同的处理方式。因此,如果要采用数字顺序排序,应该明确的给出-n选项

uniq

uniq命令通过消除重复内容,从给定输入中(stdin或命令行参数文件)找出单一的行。他也可以用来找出输入中出现的重复行。uniq只能用于排过序的数据输入,因此,uniq要么使用管道,要么将排

过序的文件作为输入,并总是以这种方式与sort命令结合起来使用。

[root@server1 test]# cat sorted.txt

bash

hack

foss

hack

hack

[root@server1 test]# uniq sorted.txt

bash

hack

foss

hack

[root@server1 test]# sort sorted.txt | uniq

bash

foss

hack

[root@server1 test]# sort -u sorted.txt

bash

foss

hack

[root@server1 test]# sort sorted.txt | uniq -u #只显示唯一的行(在输入文件中没有出现重复的行)

bash

foss

[root@server1 test]# sort sorted.txt | uniq -c  #统计各行在文件中出现的次数

1 bash

1 foss

3 hack

[root@server1 test]# sort sorted.txt | uniq -d  #找出文件中重复的行

hack

-s指定可以跳过前N个字符

-w指定用于比较的最大字符数

[root@server1 test]# cat data3.txt

u:01:gnu

d:04:linux

u:01:bash

u:01:hack

我们需要使用醒目的字符作为唯一的键。可以通过忽略前2个字符(-s 2),并使用-w选项(-w 2)指定用于比较的最大字符数的方式来选定该键。

[root@server1 test]# sort data3.txt | uniq -s 2 -w 2

d:04:linux

u:01:bash

57页-s -w -z

用uniq生成字符串样式

例如:

有一个包含重复字符的字符串,如何才能知道每个字符在字符串中出现的次数,并依照下面的格式输出字符串?

输入:ahebhaaa

输出:4a1b1e2h

# echo ahebhaaa | sed ‘s/[^.]/&\n/g’ | sed ‘/^$/d’ | sort | uniq -c | tr -d ‘ \n’

sed ‘s/[^.]/&\n/g’:在每个字符后追加一个换行符,使得每行只出现一个字符。这让我们可以用sort命令对字符进行排序。sort命令只能用于有换行符分隔的记录上

sed ‘/^$/d’:最后一个字符会被sed替换成”字符+\n”,因此会多出一个换行符并最后形成空行。这个命令就是用来删除最后的空行

tr -d ‘ \n’:将输入中的空格和换行符删除,生成所要求输出的格式

7. 分隔文件和数据

[root@server1 test]# dd if=/dev/zero bs=100k count=1 of=data.file

[root@server1 test]# split -b 10k data.file

[root@server1 test]# ls

data.file  xaa  xab  xac  xad  xae  xaf  xag  xah  xai  xaj

上面的命令将data.file分隔成多个文件,每一个文件的大小为10k。这些文件以xaa、xab、xac这样方式命名。这意味着它们都有一个字母后缀。如果想以数字为后缀,可以另外使用-d参数。此外,

使用-a -length指定后缀长度。

[root@server1 test]# split -b 10k data.file -d -a 4

[root@server1 test]# ls

data.file  x0000  x0001  x0002  x0003  x0004  x0005  x0006  x0007  x0008  x0009

上面data.file分隔后文件的前缀都有”x”。我们也可以通过提供一个前缀名以使用我们自己设定的文件名前缀。

[root@server1 test]# split -b 10k data.file -d -a 4 split_file

[root@server1 test]# ls

data.file       split_file0001  split_file0003  split_file0005  split_file0007  split_file0009

split_file0000  split_file0002  split_file0004  split_file0006  split_file0008

如果不想按照数据块大小,而是需要根据行数来分隔文件的话,可以使用-l no_of——lines行数

[root@server1 test]# split -l 10 data.file

csplit:能够根据指定的条件和字符串匹配选项对log文件进行分隔,是split工具的一个变体,split只能够根据数据大小或行数分隔文件,而csplit可以根据文本自身的特点进行分隔。是否存在某个单词或文本内容都可以作为分隔文件的条件。

[root@server1 test]# cat server.log

SERVER-1

[connection] 192.168.0.1 success

[connection] 192.168.0.2 failed

[disconnect] 192.168.0.3 pending

[connection] 192.168.0.4 success

SERVER-2

[connection] 192.168.0.1 failed

[connection] 192.168.0.2 failed

[disconnect] 192.168.0.3 success

[connection] 192.168.0.4 failed

SERVER-3

[connection] 192.168.0.1 pending

[connection] 192.168.0.2 pending

[disconnect] 192.168.0.3 pending

[connection] 192.168.0.4 failed

我们需要将这个日志文件分隔成server1.log、server2.log和server3.log,这些文件的内容分别取自原文件中不同的SERVER部分。如下:

csplit server.log /SERVER/ -n 2 -s {*} -f server -b “%02d.log”; rm server00.log

这个命令说明如下:

/SERVER/    用来匹配某一行,分隔过程即从此开始

/[REGEX]/   表示文本样式。包括从当前行(第一行)直到(但不包括)包含”SERVER”的匹配行

-n          指定分割后的文件名后缀的数字个数

-s          使命令进入静止模式,不打印其他信息

{*}         表示根据匹配重复执行分割,直到文件末尾为止。可以用{整数}的形式来指定分割执行的次数

-f          指定分割后的文件名前缀

-b          指定后缀格式。例如”%02d.log”,类似于C语言中printf的参数格式。在这里的文件名=前缀+后缀=server+%02d.log。

因为分割后的第一个文件没有任何内容(匹配的单词就位于文件的第一行中),所以我们删除了server00.log。

根据扩展名切分文件名

借助%操作符可以轻松将名称部分从”名称.扩展名”这种格式的文件名中提取出来

[root@server1 ~]# URL=www.linuxeye.com

[root@server1 ~]# echo ${URL%.*}

#删除所匹配的字符串,%属于非贪婪(non-greedy)操作。它从右到左边找出匹配通配符的最短结果。%%操作符与%相似,但行为模式却是贪婪的(greedy),这意味着它会匹配符合条件的最长的字符串

www.linuxeye

[root@server1 ~]# echo ${URL%%.*}

www

[root@server1 ~]# echo ${URL#*.}    #删除所匹配的字符串,#操作符与%类似,不过求职方向是从左向右。##操作符则用×.从左向右执行贪婪匹配。

linuxeye.com

[root@server1 ~]# echo ${URL##*.}

com

因为文件名中可能包含多个’.’字符,所以相对于#,##更适合于从文件名中提取扩展名,##执行的是贪婪匹配,因而总是能准确的提取出扩展名。

8. 交互输入自动化

[root@server1 test]# cat interactive.sh

#!/bin/bash

read -p “Enter number:” no;

read -p “Enter name:” name;

echo You have entered $no ,$name;

[root@server1 test]# echo -e “1\nhello\n” | sh interactive.sh

You have entered 1 ,hello

[root@server1 test]# echo -e “1\nhello\n” > input.data

[root@server1 test]# cat input.data

1

hello

[root@server1 test]# sh interactive.sh < input.data

You have entered 1 ,hello

expect实现自动化

参考://blog.linuxeye.com/shell/204.html

spawn        参数指定需要自动化哪一个命令

expect        参数提供需要等待的消息

send           是要发送的消息

expect eof  指明命令交互结束

上一篇:

下一篇:

相关文章