linux脚本攻略 ch2-命令之乐

1.用cat进行拼接

  • 打印文件内容
cat file1 file2 ...
  • 将文件内容和stdin拼接在一起
[work@jkz ~]$ echo 'Text through stdin' | cat text
1
2
3
[work@jkz ~]$ echo 'Text through stdin' | cat - text
Text through stdin
1
2
3
  • 去掉多余的空白行
[work@jkz ~]$ cat text
1


2

3

[work@jkz ~]$ cat -s text 
1

2

3
  • 将制表符显示为^I
[work@jkz ~]$ cat file.py 
def function():
    var = 5
        next = 6
    third=7
[work@jkz ~]$ cat -T file.py 
def function():
^Ivar = 5
^I^Inext = 6
^Ithird=7
  • 行号
[work@jkz ~]$ cat -n text 
     1  1
     2  
     3  
     4  2
     5  
     6  3
  • 利用管道重定向输出会清空输入文件
[work@jkz ~]$ cat text | cat -n > text
[work@jkz ~]$ cat text
[work@jkz ~]$ 

2.录制并回放终端会话

script命令能够录制你的击键以及击键时机,并将输入和输出结果保存在对应的文件中。scriptreplay命令可以回放会话。

[work@jkz ~]$ script -t 2> timing.log -a out.session
Script started, file is out.session
[work@jkz ~]$ tclsh
% puts [expr 2 + 2]
4
% exit
[work@jkz ~]$ exit
exit
Script done, file is out.session



[work@jkz ~]$ scriptreplay timing.log out.session
[work@jkz ~]$ tclsh
% puts [expr 2 + 2]
4
% exit
[work@jkz ~]$ exit

3.查找并列出文件

find命令的工作方式如下:沿着文件层次结构向下遍历,匹配符合条件的文件,执行相应的操作。默认的操作是打印出文件和目录,这也可以使用-print选项来指定。
find命令能够基于通配符或正则表达式、目录树深度、文件日期、文件类型等条件查找文件。

  • 列出指定目录
find path
  • -print0
    print选项使用\n(换行符)分隔输出的每个文件或目录名。而-print0选项则使用空字符'\0'来分隔。-print0的主要用法是将包含换行符或空白字符的文件名传给xargs命令。
[work@jkz  shelldir]$ echo "test" > "file name"
[work@jkz  shelldir]$ ls
file name
[work@jkz  shelldir]$ find . -type f -print | xargs ls -l
ls: cannot access ./file: No such file or directory
ls: cannot access name: No such file or directory
[work@jkz  shelldir]$ find . -type f -print0 | xargs -0 ls -l
-rw-rw-r-- 1 work work 5 Dec 13 07:43 ./file name
  • 根据文件名或正则表达式进行搜索
    -name选项指定了待查找文件名的模式。这个模式可以是通配符,也可以是正则表达式。
    -iname(忽略字母大小写)。
    find命令支持逻辑操作符。-a和-and选项可以执行逻辑与(AND)操作,-o和-or选项可以执行逻辑或(OR)操作。
    -path选项可以限制所匹配文件的路径及名称。
    -regex选项和-path类似,只不过前者是基于正则表达式来匹配文件路径的。-iregex忽略大小写。
find . \( -name '*.sh' -o -name '*.log' \)
[work@jkz  ~]$ find . -path '*/shell*/*' -name '*.txt' 
./shelldir/hello.txt
find . -regex '.*\(\.sh\|\.log\)$'
  • 注意*.txt两边的单引号。shell会扩展没有引号或是出现在双引号(")中的通配符。单引号能够阻止shell扩展*.txt,使得该字符串能够原封不动地传给find命令。

  • 否定参数

find . ! -path '*apache*'
  • 基于目录深度的搜索
    默认情况下,find命令不会跟随符号链接。-L选项可以强制其改变这种行为。但如果碰上了指向自身的链接,find命令就会陷入死循环中。
    -maxdepth和–mindepth选项可以限制find命令遍历的目录深度。
    -maxdepth和-mindepth应该在find命令中及早出现。如果作为靠后的选项,有可能会影响到find的效率,因为它不得不进行一些不必要的检查。

  • 根据文件类型搜索
    find命令可以使用-type选项对文件类型进行过滤。


  • 根据文件的时间戳进行搜索
    访问时间(-atime):用户最近一次访问文件的时间。
    修改时间(-mtime):文件内容最后一次被修改的时间。
    变化时间(-ctime):文件元数据(例如权限或所有权)最后一次改变的时间。

打印出在最近7天内被访问过的所有文件:
find . -type f -atime -7
打印出恰好在7天前被访问过的所有文件:
find . -type f -atime 7
打印出访问时间超过7天的所有文件:
find . -type f -atime +7

find命令还支持以“分钟”为计时单位的选项:
-amin/-mmin/-cmin

–newer选项可以指定一个用于比较修改时间的参考文件,然后找出比参考文件更新的(更近的修改时间)所有文件。
找出比file.txt修改时间更近的所有文件:
find . -type f -newer file.txt

  • 基于文件大小的搜索
    大于2KB的文件:
    find . -type f -size +2k
    小于2KB的文件:
    find . -type f -size -2k
    大小等于2KB的文件:
    find . -type f -size 2k
    其他文件大小单位:
    b:块(512字节)。
    c:字节。
    w:字(2字节)。
    k:千字节(1024字节)。
    M:兆字节(1024K字节)。
    G:吉字节(1024M字节)。

  • 基于文件权限和所有权的匹配
    -perm选项指明find应该只匹配具有特定权限值的文件。
    打印出权限为644的文件:
    find . -type f -perm 644
    也可以根据文件的所有权进行搜索。用选项 -user USER就能够找出由某个特定用户所拥有的文件。
    find . -type f -user root

  • 利用find执行相应操作
    find命令能够对其所查找到的文件执行相应的操作。
    删除匹配的文件,find命令的-delete选项可以删除所匹配到的文件。
    从当前目录中删除.swp文件:
    find . -type f -name "*.swp" -delete

利用-exec选项,find命令可以结合其他命令使用。find命令使用一对花括号{}代表文件名。对于每一个匹配的文件,find命令会将{}替换成相应的文件名并更改该文件的所有权。
find . -type f -user root -exec chown work {} ;
注意该命令结尾的;。必须对分号进行转义,否则shell会将其视为find命令的结束,而非chown命令的结束。
为每个匹配到的文件调用命令可是个不小的开销。如果指定的命令接受多个参数(如chown),你可以换用加号(+)作为命令的结尾。这样find会生成一份包含所有搜索结果的列表,然后将其作为指定命令的参数,一次性执行。

将给定目录中的所有C程序文件拼接起来写入单个文件all_c_files.txt:
$ find . -type f -name '*.c' -exec cat {} ;>all_c_files.txt
$ find . -type f -name '*.c' -exec cat {} > all_c_files.txt ;
$ fine . -type f -name '*.c' -exec cat {} >all_c_files.txt +
没有使用>>(追加)的原因是find命令的全部输出就只有一个数据流(stdin)

将10天前的 .txt文件复制到OLD目录中:
find . -type f -mtime +10 -name "*.txt" -exec cp {} OLD ;

-exec选项只能够接受单个命令。不过可以把多个命令写到一个shell 脚本中( 例如command.sh),然后在-exec中使用这个脚本:
-exec ./commands.sh {} ;

  • 让find跳过特定的目录
    在搜索时排除某些文件或目录的技巧叫作修剪。使用-prune选项排除某些符合条件的文件。
    跳过apache-tomcat-8.5.35目录:
    find ./ -name 'apache-tomcat-8.5.35' -prune -o -type f

4.玩转xargs

调用只能接受命令行参数命令的方法:

  • 利用管道
  • 使用反引号执行命令,然后将其输出作为命令行参数
    如果处理文件过多,会报错。
gcc `find '*.c'`
  • 使用xargs命令。(xargs命令可以解决反引号的问题)

xargs命令应该紧跟在管道操作符之后。它使用标准输入作为主要的数据源,将从stdin中读取的数据作为指定命令的参数并执行该命令。

  • 在一组sh中搜索字符串bash
    ls *.sh | xargs grep bash

  • xargs命令重新格式化stdin接收到的数据,再将其作为参数提供给指定命令。xargs默认会执行echo命令。和find命令的-exec选项相比,两者在很多方面都相似。

  • 将多行输入转换成单行输出。
    cat test | xargs

  • 将单行输入转换成多行输出。xargs的-n选项可以限制每次调用命令时用到的参数个数。下面的命令将输入分割成多行,每行n个元素:

[work@jkz ~]$ cat text | xargs -n 3
1 2 3
4 5 6
7 8 9
10 11 12
[work@jkz ~]$ cat text | xargs -n 4
1 2 3 4
5 6 7 8
9 10 11 12

工作原理
xargs命令接受来自stdin的输入,将数据解析成单个元素,然后调用指定命令并将这些元素作为该命令的参数。xargs默认使用空白字符分割输入并执行/bin/echo。

  • 如果文件或目录名中包含空格(甚至是换行)的话,使用空白字符来分割输入就会出现问题。
    但是可以定义一个用来分隔参数的分隔符。-d选项可以为输入数据指定自定义的分隔符
[work@jkz ~]$ echo "splitXsplit2Xsplit3Xsplit4" | xargs -d X
split split2 split3 split4
  • xargs命令可以同find命令很好地结合在一起。find的输出可以通过管道传给xargs,由后者执行-exec选项所无法处理的复杂操作。如果文件系统的有些文件名中包含空格,find命令的-print0选项可以使用0(NULL)来分隔查找到的元素,然后再用xargs对应的-0选项进行解析。下面的例子在Samba挂载的文件系统中搜索.docx文件,这些文件名中通常会包含大写字母和空格。其中使用了grep找出内容中不包含image的文件:
    find /smbMount -iname '*.docx' -print0 | xargs -0 grep -L image

解释:使用0以后,对于找到的文件名含有空格的元素,会被当做一个整体传给xargs。xargs同理是以0作为分隔符,所以不会出现误操作。其实这本是上也是协议,find作为发送端,xargs作为接收端。双方要协商好处理的协议。

  • 默认情况下,由xargs添加的参数都被放置在指定命令的尾部。
    cat args.txt | xargs -n 2 ./cecho.sh

  • xargs有一个选项-I,可以用于指定替换字符串,这个字符串会在xargs解析输入时被参数替换掉。如果将-I与xargs结合使用,对于每一个参数,指定命令只会执行一次。-I {}指定了替换字符串,命令以循环的方式执行。为该命令提供的各个参数会通过stdin读取并依次替换掉字符串{}。

[work@jkz ~]$ cat args.txt | xargs -I {} ./cecho.sh -p {} -l
-p arg1 -l#
-p arg2 -l#
-p arg3 -l#
  • xargs和find可以配合完成任务。不过在结合使用的时候需要留心。
    find . -type f -name "*.txt" -print | xargs rm -f
    这样做很危险,有可能会误删文件。我们无法预测find命令输出的分隔符究竟是什么(究竟是'\n'还是' ')。如果有文件名中包含空格符(' '),xargs会将其误认为是分隔符。
    更安全的做法:使用find命令的-print0选项生成以空字符('\0')作为分隔符的输出,然后将其作为xargs命令的输入。
    find . -type f -name "*.txt" -print0 | xargs -0 rm -f

  • 统计源代码目录中所有C程序文件的行数
    find source_code_dir_path -type f -name "*.c" -print0 | xargs -0 wc –l

  • 结合stdin,巧妙运用while语句和子shell
    xargs会将参数放置在指定命令的尾部,因此无法为多组命令提供参数。可以通过创建子shell来处理这种复杂情况。
    $ cat files.txt | ( while read arg; do cat $arg; done )
    # 等同于cat files.txt | xargs -I {} cat {}
    在while循环中,可以将cat $arg替换成任意数量的命令,这样我们就可以对同一个参数执行多条命令。也可以不借助管道将输出传递给其他命令。这种利用()创建子shell的技巧可以应用于各种问题场景。
    shell的-c选项可以调用子shell来执行命令行脚本。它可以与xargs结合解决多次替换的问题。下列命令找出了所有的shell文件并显示出每个文件的名字,文件名前会加上一个换行符(-e选
    项允许进行转义替换)。在文件名之后是该文件中含有bash的所有行:
    find . -name '*.sh' | xargs -I ^ sh -c "echo -ne ' ^: '; grep bash ^"

5.用tr进行转换

tr可用于编写优雅的单行命令。tr可以对来自标准输入的内容进行字符替换、字符删除以及重复字符压缩。tr是translate(转换)的简写,因为它可以将一组字符转换成另一组字符。

tr只能通过stdin(标准输入)接收输入(无法通过命令行参数接收)。
调用格式:
tr [options] set1 set2
来自stdin的输入字符会按照位置从set1映射到set2(set1中的第一个字符映射到set2中的第一个字符,以此类推),然后将输出写入stdout(标准输出)。如果两个字符组的长度不相等,那么set2会不断复制其最后一个字符,直到长度与set1相同。如果set2的长度大于set1,那么在set2中超出set1长度的那部分字符则全部被忽略。

[work@jkz ~]$ echo "HELLO WORLD" | tr 'A-Z' 'a-z'
hello world

'ABD-}'、'aA.,'、'a-ce-x'以及'a-c0-9'等均是合法的集合。使用“起始字符-终止字符”这种格式。这种写法也可以和其他字符或字符类结合使用。如果“起始字符-终止字符”不是有效的连续字符序列,那么它就会被视为含有3个元素的集合(起始字符、-和终止字符)。也可以使用像'\t'、'\n'这种特殊字符或其他ASCII字符。
将制表符转换成单个空格:

tr '\t' ' ' < file.txt
  • 删除字符
    选项-d,可以通过指定需要被删除的字符集合,将出现在stdin中的特定字符清除掉:
[work@jkz ~]$ echo "hello 123 world 356" | tr -d '0-9'
hello  world 
  • 字符组补集
    利用选项-c来使用set1的补集。
    只给出了set1,那么tr会删除所有不在set1中的字符。
[work@jkz ~]$ echo "hello 1 char 2 next 4" | tr -d -c '0-9 \n'
 1  2  4

也给出了set2,tr会将不在set1中的字符转换成set2中的字符。

[work@jkz ~]$ echo "hello 1 char 2 next 4" | tr -c '0-9' '-'
------1------2------4-
  • 压缩字符
    可以删除字符串中重复出现的字符。
    tr -s '[需要被压缩的一组字符]'
[work@jkz ~]$ echo "GNU is not UNIX. Recursive right ?" | tr -s ' '
GNU is not UNIX. Recursive right ?

用tr以一种巧妙的方式将文件中的数字列表进行相加

[work@jkz ~]$ cat sum.txt 
1
2
3
4
5
[work@jkz ~]$ cat sum.txt | echo $[$(tr '\n' '+') 0]
15

tr命令将'\n'替换成了'+',我们因此得到了字符串1+2+3+..5+,但是在字符串的尾部多了一个操作符+。为了抵消这个多出来的操作符,我们再追加一个0。
$[ operation ]执行算术运算。

  • 字符类
    alnum:字母和数字。
    alpha:字母。
    cntrl:控制(非打印)字符。
    digit:数字。
    graph:图形字符。
    lower:小写字母。
    print:可打印字符。
    punct:标点符号。
    space:空白字符。
    upper:大写字母。
    xdigit:十六进制字符。
    示例:
    tr '[:lower:]' '[:upper:]'

6.校验和与核实

校验和能够让我们核实文件中所包含的数据是否和预期的一样。
强健性最好且使用最为广泛的校验和算法是MD5和SHA-1。
md5sum是一个长度为32个字符的十六进制串。

[work@jkz ~]$ md5sum text > text.md5
[work@jkz ~]$ md5sum -c text.md5 
text: OK
[work@jkz ~]$ vim text
[work@jkz ~]$ md5sum -c text.md5 
text: FAILED
md5sum: WARNING: 1 computed checksum did NOT match
[work@jkz ~]$ cat text.md5 
714a63efb5a21cd8786c59e3ec1f892f  text

SHA-1是另一种常用的校验和算法。它从给定的输入中生成一个长度为40个字符的十六进制串。用来计算SAH-1校验和的命令是sha1sum,其用法和md5sum的类似。只需要把先前讲过的那些命令中的md5sum改成sha1sum就行了,记住将输出文件名从file_sum.md5改为file_sum.sha1。

[work@jkz ~]$ sha1sum text > text.sha1 
[work@jkz ~]$ cat text.sha1 
700634a38bdc7b886fc43e6956a1b344083c99eb  text
[work@jkz ~]$ sha1sum -c text.sha1 
text: OK
[work@jkz ~]$ vim text
[work@jkz ~]$ sha1sum -c text.sha1 
text: FAILED
sha1sum: WARNING: 1 computed checksum did NOT match
  • 对目录进行校验
    md5deep或sha1deep命令可以遍历目录树,计算其中所有文件的校验和。

  • md5与SHA-1都是单向散列算法,均无法逆推出原始数据。这种类型的散列算法多用于存储密码。密码只存储其对应散列值。如果需要认证某个用户,则读取该用户提供的密码并转换成散列值,然后与之前存储的散列值进行比对。如果相同,用户则通过认证并授权访问。

  • 尽管应用广泛,md5sum和SHA-1已不再安全,因为近年来计算能力的攀升使其变得容易被破解。推荐使用bcrypt或sha512sum这类工具进行加密。更多信息可参看https://codahale.com/how-to-safely-store-a-password/。

  • shadow-like散列(加盐散列)
    shadow密码通常都是加盐密码(salted password)。所谓的“盐”(SALT)就是一个额外的字符串,起混淆的作用,使加密更加难以破解。盐是由一些随机位组成的,它们作为密钥生成函数的输入之一,产生密码的加盐散列。
    https://en.wikipedia.org/wiki/Salt_(cryptography)

7.加密工具与散列

加密技术主要用于防止数据遭受未经授权的访问。和上面讲的校验和算法不同,加密算法可以无损地重构原始数据。

  • crypt命令通常并没有安装在Linux系统中。它是一个简单的加密工具,相对而言不是那么安全。该命令从stdin接受输入,要求用户创建口令,然后将加密数据输出到 stdout.

  • gpg(GNU privacy guard,GNU隐私保护)是一种应用广泛的工具,它使用加密技术来保护文件,以确保数据在送达目的地之前无法被读取。

  • Base64是一组相似的编码方案,它将二进制数据转换成以64为基数的形式(radix-64 representation),以可读的ASCII字符串进行描述。

8.行排序

sort命令能够对文本文件和stdin进行排序。它可以配合其他命令来生成所需要的输出。uniq经常与sort一同使用,提取不重复(或重复)的行。

sort file1.txt file2.txt > sorted.txt
sort file1.txt file2.txt -o sorted.txt
按照数字顺序:
sort -n file.txt
逆序:
sort -r file.txt
按照月份排序:
sort -M months.txt
合并两个已排序过的文件:
sort -m sorted1 sorted2
找出已排序文件中不重复的行,uniq要求数据必须经过排序:
sort file1.txt file2.txt | uniq
检查文件是否已经排序过:
sort -c filename
echo $?
根据哪一列进行排序,通过-k指定,列与列之间由空格分隔。:
sort -k 2 data.txt
终止符\0用来确保安全地使用xargs命令:
sort -z data.txt | xargs -0
选项-b用于忽略文件中的前导空白行,选项-d用于指明以字典序进行排序:
sort -bd unsorted.txt

uniq命令可以从给定输入中(stdin或命令行参数指定的文件)找出唯一的行,报告或删除那些重复的行。
uniq只能作用于排过序的数据,因此,uniq通常都与sort命令结合使用。

统计各行在文件中出现的次数:
sort unsorted.txt | uniq -c
找出文件中重复的行:
sort unsorted.txt | uniq -d
可以结合-s和-w选项来指定键,-s 指定跳过前N个字符,-w 指定用于比较的最大字符数

将命令输出作为xargs命令的输入时,最好为输出的各行添加一个0值字节(zero-byte)终止符。因为在默认情况下,xargs命令会用空格来分割参数。

-z选项可以生成由0值字节终止的输出:
uniq -z file.txt

9.临时文件命名与随机数

最适合存储临时数据的位置是 /tmp(该目录中的内容在系统重启后会被清空)。
临时文件:

$ filename=`mktemp`
$ echo $filename
/tmp/tmp.8xvhkjF5fH

临时目录:

$ dirname=`mktemp -d`
$ echo $dirname
tmp.NI8xzW7VRX

仅仅是想生成文件名,不希望创建实际的文件或目录:

$ tmpfile=`mktemp -u`
$ echo $tmpfile
/tmp/tmp.RsGmilRpcT

基于模板创建临时文件名:

$mktemp test.XXX
test.2tc

如果提供了定制模板,X会被随机的字符(字母或数字)替换。注意,mktemp正常工作的前提是保证模板中至少要有3个X。

10.分割文件与数据

split命令可以用来分割文件。该命令接受文件名作为参数,然后创建出一系列体积更小的文件,其中依据字母序排在首位的那个文件对应于原始文件的第一部分,排在次位的文件对应于原始文件的第二部分,以此类推。

在split命令中,除了k(KB),我们还可以使用M(MB)、G(GB)、c(byte)和w(word):
split -b 10k data.file
split默认使用字母后缀。如果想使用数字后缀,需要使用-d选项。此外, -a length
可以指定后缀长度:
split -b 10k data.file -d -a 4
为分割后的文件指定文件名前缀,可以通过提供一个前缀作为最后一个参数来实现:split -b 10k data.file -d -a 4 split_file
根据行数来分割文件的话,可以使用 -l no_of_lines:
split -l 10 data.file

csplit实用工具能够基于上下文来分隔文件。它依据的是行计数或正则表达式。这个工具对于日志文件分割尤为有用。

  • /SERVER/ 用来匹配特定行,分割过程即从此处开始。
    /[REGEX]/ 用于描述文本模式。它从当前行(第一行)一直复制到(但不包括)包含SERVER的匹配行。
    {*} 表示根据匹配重复执行分割操作,直到文件末尾为止。可以用{整数}的形式来指定分割执行的次数。
    -s 使命令进入静默模式,不打印其他信息。
    -n 指定分割后的文件名后缀的数字个数,例如01、02、03等。
    -f 指定分割后的文件名前缀(在上面的例子中,server就是前缀)。
    -b 指定后缀格式。例如%02d.log,类似于C语言中printf的参数格式。在这里:文件名 = 前缀 + 后缀,也就是server + %02d.log。
[work@jkz ~]$ 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

[work@jkz ~]$ csplit server.log /SERVER/ -n 2 -s {*} -f server -b "%02d.log"
[work@jkz ~]$ ls |grep server
server00.log
server01.log
server02.log
server03.log
server.log
[work@jkz ~]$ cat server00.log 
[work@jkz ~]$ cat server01.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
[work@jkz ~]$ 

11.根据扩展名切分文件名

借助%操作符可以从name.extension这种格式中提取name部分(文件名):

[work@jkz ~]$ file="sample.jpg"
[work@jkz ~]$ name=${file%.*}
[work@jkz ~]$ echo $name
sample

#操作符可以提取出扩展名:

[work@jkz ~]$ extension=${file#*.}
[work@jkz ~]$ echo $extension 
jpg

${VAR%.*} 的含义如下:

  • 从 $VAR中删除位于%右侧的通配符(在上例中是.*)所匹配的字符串。通配符从右向左进行匹配。
  • 给VAR赋值,即VAR=sample.jpg。通配符从右向左匹配到的内容是.jpg,因此从$VAR中删除匹配结果,得到输出sample。

%属于非贪婪(non-greedy)操作。它从右向左找出匹配通配符的最短结果。还有另一个操作符%%,它与%相似,但行为模式却是贪婪的,这意味着它会匹配符合通配符的最长结果。

[work@jkz ~]$ VAR=hack.fun.book.txt
[work@jkz ~]$ echo ${VAR%.*}
hack.fun.book
[work@jkz ~]$ echo ${VAR%%.*}
hack

#操作符与%类似,不过求值方向是从左向右。
${VAR#*.}的含义如下:

  • 从$VAR中删除位于#右侧的通配符(即在上例中使用的*.)从左向右所匹配到的字符串。

#也有一个对应的贪婪操作符##:

[work@jkz ~]$ echo ${VAR#*.}
fun.book.txt
[work@jkz ~]$ echo ${VAR##*.}
txt

12.多个文件的重命名与移动

mv可以进行重命名。

rename命令利用Perl正则表达式修改文件名。组合find、rename和mv命令,我们能做到的事其实很多。

  • 将 *.JPG更名为 *.jpg:
    rename *.JPG *.jpg
  • 将文件名中的空格替换成字符 "_":
    rename 's/ /_/g' *
  • 转换文件名的大小写:
    rename 'y/A-Z/a-z/' *
    rename 'y/a-z/A-Z/' *
  • 将所有的.mp3文件移入给定的目录:
    find path -type f -name "*.mp3" -exec mv {} target_dir ;
  • 以递归的方式将所有文件名中的空格替换为字符"_":
    find path -type f -exec rename 's/ /_/g' {} ;

13.拼写检查与词典操作

大多数Linux发行版都含有一份词典文件。还有一个叫作aspell的命令行实用工具,其作用是进行拼写检查。

look命令可以显示出以特定字符串起始的行。你可以用它在日志文件中查找以特定日期为首的记录,或是在词典中查找以特定字符串开头的单词。look默认会搜索/usr/share/dict/words,你也可以给出文件供其搜索。

14.交互输入自动化

可以创建自己的交互式shell脚本。对于普通用户而言,相较于记忆命令行参数及其正确的顺序,同一系列提示信息打交道要更容易。
交互式脚本:

$ backup.sh
What folder should be backed up? notes
What type of files should be backed up? .docx

实现:

#!/bin/bash
# backup.sh
# 使用后缀备份文件。不备份以~开头的临时文件
read -p " What folder should be backed up: " folder
read -p " What type of files should be backed up: " suffix
find $folder -name "*.$suffix" -a ! -name '~*' -exec cp {} \
$BACKUP/$LOGNAME/$folder
echo "Backed up files from $folder to $BACKUP/$LOGNAME/$folder"

自动输入:

$ echo -e "notes\ndocx\n" | ./backup.sh
Backed up files from notes to /BackupDrive/MyName/notes

echo命令和重定向可以实现交互式输入的自动化。但这种技术存在问题,因为输入内容没有经过验证,我们认定目标应用总是以相同的顺序接收数据。但如果程序要求的输入顺序不同,或是对某些输入内容不做要求,那就要出岔子了。

expect程序能够执行复杂的交互操作并适应目标应用的变化。该程序在世界范围内被广泛用于控制硬件测试、验证软件构建、查询路由器统计信息等。
expect是一个和shell类似的解释器。它基于TCL语言。我们将讨论如何使用spawn、expect和send命令实现简单的自动化。借助于TCL语言的强大功能,expect能够完成更为复杂的任务。

下面的例子会先执行备份脚本,然后查找模式folderfile,以此确定备份脚本是否要求输入目录名或文件名并作出相应的回应。如果重写备份脚本,要求先输入备份文件类型,后输入备份目录,这个自动化脚本依然能够应对。

#!/usr/bin/expect
#文件名: automate_expect.tcl
spawn ./backup.sh
expect {
  "*folder*" {
    send "notes\n"
    exp_continue
  }
  "*type*" {
    send "docx\n"
    exp_continue
  }
}

spawn命令的参数是需要自动化运行的应用程序及其参数。
expect命令接受一组模式以及匹配模式时要执行的操作。操作需要放入花括号中。
send命令是要发送的信息。和echo -n -e类似,send不会自动添加换行符,也能够理解转义字符。

15.利用并行进程加速命令执行

parallel命令从stdin中读取文件列表,使用类似于find命令的-exec选项来处理这些文
件。符号{}代表被处理的文件,符号{.}代表无后缀的文件名。
ls *jpg | parallel convert {} -geometry 50x50 {.}Small.jpg

16.检查目录以及其中的文件与子目录

  • 生成目录的树状视图
    find . -exec sh -c 'echo -n {} | tr -d "[:alnum:]_.-" | tr "/" " "; basename {}' ;

  • 生成文件及子目录的汇总信息

for d in `find . -type d`;
do
echo `find $d -type f | wc -l` files in $d;
done

你可能感兴趣的:(linux脚本攻略 ch2-命令之乐)