读《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实现自动化
    参考://linuxeye.com/shell/204.html
    spawn        参数指定需要自动化哪一个命令
    expect        参数提供需要等待的消息
    send           是要发送的消息
    expect eof  指明命令交互结束

    • 本文由 发表于 2012-12-05
    • 转载请务必保留本文链接:https://linuxeye.com/243.html
    Linux下内置命令和外部命令详解 Linux

    Linux下内置命令和外部命令详解

    Linux的命令分为内部命令和外部命令: 内部命令在系统启动时就调入内存,是常驻内存的,所以执行效率高。 外部命令是系统的软件功能,用户需要时才从硬盘中读入内存。 type可以用来判断一个命令是否为内...
    shell字符串处理 脚本

    shell字符串处理

    字符串操作(长度,读取,替换) 表达式 含义 ${#string} $string的长度 ${string:position} 在$string中, 从位置$position开始提取子串 ${stri...
    Linux shell用法和技巧 脚本

    Linux shell用法和技巧

    检查远程端口是否对bash开放: echo >/dev/tcp/8.8.8.8/53 && echo "open" 让进程转入后台: Ctrl + z 将进程转到前台: fg 产...
    匿名

    发表评论

    匿名网友