SHELL笔记(二)

1 录制并回放终端会话
1.1录制终端
  $ script -t 2> timing.log -a output.session
    commands;
    ......
   .......
   exit

//文件timing.log用于保存时序信息;文件output.session用于存储命令输出。

1.2回放终端
   $ scriptreplay timing.log output.session



 2.文件查找和文件列表
 2.1列出当前目录及子目录下所有的文件和文件夹
     eg: find /home/mapgoo/shell
 
      (  -print  指明打印出匹配文件的文件名(路径)。当使用-print时,'\n'作为用于对输出的文件名进行分隔。就算忽略-print,find命令仍会打印出文件名。
         -print0 指明使用'\0' 作为匹配的文件名之间的定界符。当文件名中包含换行符时,这个方法就有用武之地了。)

2.2根据文件名或正则表达式进行搜索 
    eg:  find  /home/mapgoo -name   "*.txt"    // 在指定目录下搜索以.txt结尾的文件(
 -iname 是忽略大小写) 
    eg : find /home/mapgoo \( -name "*.txt" -o -name "*.pdf" \)    //多个条件搜索(OR)
    eg: find /home/mapgoo ! -name "*.txt"  //搜索不以.txt结尾的文件

    2.3基于目录深度搜索(为了不影响效率,当作为第三个参数)
   eg: find  /home/mapgoo  -maxdepth 2 -name "f*"    //  
 -maxdepth 2最多向下遍历两级目录
   eg:   find  /home/mapgoo  -mindepth 2 -name "f*"    //  -mindepth 2深度为2的目录开始搜索

   2.4按文件类型搜索
  eg: find /home/mapgoo  -type d    //列出所有目录
  eg:   
find /home/mapgoo  -type f     //列出普通文件
   (type参数有: 普通文件 f ;   符号链接  l ;  目录   d ; 字符设备  c ;  块设备 b ; 套接字 s ;  FIFO  p )

  2.5按文件时间搜索 
   eg: find .  -type f  -atime -7  //打印出最近7天内被访问过的所有文件
   eg: find . -type f -atime 7    //打印出恰好在7天前被访问过的所有文件
   eg: find .  -type f -atime  +7    //打印出访问时间超过7天的所有文件
   (单位为“天” : -atime  访问时间(用户最近一次访问文件的时间);
                            -mtime  修改时间(文件内容最后一次被修改的时间);
                            -ctime    变化时间(文件元数据最后一次改变的时间,例如权限或所有权)
      单位为“分钟”:  -amin 访问时间;  -mmin 修改时间;  -cmin 变化时间 )

 eg: find . -type f -newer file.txt   //找出比file.txt修改时间更近的所有文件

  
   2.6基于文件大小的搜索
    eg:  find . -type -f -size +2k  //大于2kb的文件
    eg : find -type f -size -2k   //小于2kb的文件
    eg : find -type f -size 2k     //等于2kb的文件
   (k  1024字节;   b   块,即512字节;   c  字节;  w  字,即2字节;   M   1024K字节; G  1024M字节)

2.7删除匹配的文件
 eg :  find . -type f  -name "*.swp" -delete   //删除当前目录下所有的.swp文件

2.8基于文件权限和所有权的匹配
eg: find . -type f  -perm 644    // 打印出权限为644的文件
eg: find . -type f  -user slynux  //打印出用户slynux拥有的所有文件(-user的参数可以是用户名或UID)

2.9利用find执行命令或动作
eg: find . -type -f -user root -exec chown www-data {} \;   //找到属于root的文件将其所有权改成www-data
         (在这个命令中,{}是一个与-exec选项搭配的特殊字符串,对于每一个匹配的文件,{}会被替换成相应的文件名。如找到文件test1.txt和test2.txt;那么chown www-data会被解析成: chown www-data test1.txt 和 chown www-data test2.txt )

eg: find . -type f -name "*.c" -exec cat {} \; >all_c_file.txt    //将制定目录中所有的c程序文件拼接起来写入单个文件all_c_files.txt
                   ( 在这里使用>操作符,而没有使用>>的原因是find命令的全部输出就只有一个数据流stdin)

eg: find . -type f -mtime +10 -name "*.txt" -exec cp {} OLD \;  //将10天前的.txt文件复制到OLD目录中
            (我们无法在-exec参数中直接使用多个命令。但可以将多个命令写入脚本中,然后使用这个脚本: -exec ./command.sh {} \; )

2.10让find跳过特定的目录
   eg: find . \( -name ".git" -prune \) -o \( -type f print \)   //排除.git文件进行查找



3.xargs  (该命令应该紧跟在管道操作符之后,以标准输入作为主要的源数据流; xargs命令把从stdin接收到的数据重新格式化,再将其作为参数提供给其他命令。)
command | xargs

3.1eg: cat example.txt | xargs   //将多行输入转换成单行输出。
        (只需要将换行符移除,再用“ ”空格进行代替,就可以实现多行输入的转换。 '\n'被解释成一个换行符,换行符其实就是多行文本之间的定界符。利用xargs,我们可以用空格替换掉换行符。这样就能够多行文本转换成单行文本)

3.2eg: cat example.txt | xargs -n 3    // 将单行输入转换成多行输出。(指定每行最大的参数数量n,我们可以将任何来自stdin的文本换分成多行,每行n个参数。每个参数都是有" "空格隔开的字符串。空格是默认的定界符。)

3.3 eg : echo "splitXsplitXsplit" | xargs -d X -n 2    //用-d选项为输入指定一个定制的定界符

3.4 eg: cat args.txt | xargs  ./cecho.sh   //读取stdin,将格式化参数传递给命令  

3.5 eg: cat args.txt | xargs -I {} ./cecho.sh -p {} -l   // -I {} 指定了替换字符串。对于每一个命令参数,字符串{}都会被从stdin读取到参数换掉  (使用-I的时候,命令以循环的方式执行,如果有3个参数,那么命令就会连同{}一起被执行3次。在每次的执行中都会被替换为相应的参数)

3.6结合find使用xargs
eg: find . -type f -name "*.txt" -print0 | xargs -0 rm -f   //用find匹配并列出所有的.txt文件,然后用xargs将这些文件删除;(只要我们把find的输出作为xargs的输入,就必须将-print0与find结合使用,以字符NULL('\0')来分隔输出。 xargs -0将\0作为输入定界符。)

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

3.8结合stdin,巧妙运用while语句和子shell
 eg: cat  file.txt | (while read arg; do cat $arg; done)    //等同于cat file.txt | xargs -I {} cat {}
    (由于xargs只能以有限的几种方式提供参数而且它不能为多组命令提供参数,要执行包含来自标准输入的多个参数的命令,用该方法,在while循环中,可以将cat $arg替换成任意数量的命令,这样我们就可以对同一个参数执行多条命令。也可以不借助管道,将输出传递给其他命令。这个技巧能够适用于各种问题场景。)


4. 用tr进行转换 
 (可以对来自标准输入的内容进行字符替换,字符删除以及重复字符压缩。)
  格式: tr [options] set1 set2   (将来自stdin的输入字符从set1映射到set2,然后将输出写入stdout。set1和set2是字符类或字符集。如果两个字符集的长度不相等,set1>set2,那么set2会不断重复最后一个字符,指导长度相同;如果set1<set2;那么set2中超出部分字符则全部被忽略。)

4.1 eg: echo "HELLO world“ | tr  'A-Z'  'a-z'   //将输入字符由大写转换成小写

4.2通过tr对数字进行加密和解密
      加密:  echo 123456 | tr '0-9' '9876543210'     //结果876543
      解密:  echo 876543 | tr '9876543210' '0-9'   //结果123456

4.3 ROT13加密 (加密和解密用的同一个函数)
      加密:  echo "try my best" | tr 'a-zA-Z' 'n-za-mN-ZA-M'   //结果 gel zl orfg
      解密:  
echo "gel zl orfg" | tr 'a-zA-Z' 'n-za-mN-ZA-M'    //结果   try my best
 
4.4  用tr删除字符
    eg:  echo  " hello 123 world 123" |  tr -d '0-9'    //结果 hello world  (只是用set1,不试用set2)

4.5 字符集补集 

  eg: echo " hello 1 char 2 next 3" | tr -d -c '0-9 \n'   //结果  1  2  3 (从输入文本中将不在补集中的所有字符全部删除)

4.6 用tr压缩字符 (多数情况下,连续的重复字符应该压缩成单个字符 -s 选项)
   eg:echo "GNU   is   not Unix" | tr -s ' '  //结果 GNU is not Unix (去掉多余的空格)

4.7 eg: cat shuzi.txt | echo $[ $( tr '\n' '+') 0]   //shuzi.txt中每行的数字相加 (tr用来将'\n'替换成'+',因此得到“1+2+3+4+”,但尾部多了一个操作符+,为抵消最后追加一个0 )

4.8字符类  (tr可以像使用集合一样使用各种不同的字符类。 alnum 字母和数字; alpha 字母; cntrl  控制(非打印)字符;digit  数字;graph 图形字符; lower 小写字母; print 可打印字符; punct 标点符号; space 空白字符; upper 大写字母; xdigit 十六进制字符。)
 eg: tr  '[:lower:]'  '[:upper:]'


5.校验和 与 核实 (用于文件完整性测试的特定密钥就称为校验和md5sum。 最知名且使用最为广泛的校验和技术是md5sum和SHA-1 。)
   5.1对文件
eg: md5sum filename > file_sum.md5    //md5sum是一个32个字符的十六进制串(这里保存在file_sum.md5文件中)
     核实:   md5sum -c file_sum.md5    //传输后的文件要在同一个目录中

eg: sha1sum filename >file_sum.sha1  //SHA-1是一个40个字符的十六进制串,与md5sum用法相似
    核实:   sha1sum -c  file_sum.sha1  

 5.2 对目录(md5deep  sha1deep)
  (对目录计算校验和意味着我们需要对目录中的所有文件以递归的方式进行计算)
   eg: md5deep  -rl directory_path >directory.md5  // -r使用递归的方式; -l 使用相对路径。默认情况下,md5deep会输出文件的绝对路径
       (等价于:    find directory_path -type f -print0 | xargs -0 md5sum >> directory.md5 )
    核实: md5sum -c directory.md5


6.加密工具 与 散列
    (加密技术主要用于防止数据遭受未经授权的访问。)
6.1 crypt   (是一个简单的加密工具。它从stdin接受一个文件以及口令作为输入,然后将加密数据输出到stdout。因此要对输入,输出文件使用重定向。)
加密eg:crypt < caiwenjie.sh >cryptfile  //将文件caiwenjie.sh通过crypt方式加密成cryptfile (crypt  123456 < caiwenjie.sh >cryptfile 直接将加密密钥123456写出)
 解密eg: crypt 123456 -d < cryptfile > output-caiwenjie   //将文件cryptfile解密为文件output-caiwenjie,密钥为123456

6.2 gpg (GNU隐私保护,是一种广泛的工具,它使用加密技术来保护文件,以确保数据在送达目的地之前无法被读取。)
   加密eg:gpg -c filename    //采用交互方式读取口令,并生成filename.gpg
   解密eg: gpg filename.gpg   

6.3 Base64 (它将ASCII字符转换成以64为基数的形式,以可读的ASCII字符串来描述二进制数据)
    加密eg:  base64 filename >outputfile   //等价于 cat file | base64 >outputfile
    解密eg:  base64 -d file > outputfile  //等价于 cat base64_file | base64 -d > outputfile

6.4 md5sum 与 sha1sum  (都是单向散列算法,均无法逆推出原始数据,通常用于验证数据完整性或为特定数据生成唯一的密钥)
   见第5点

6.5 shadow-like散列(salt散列)
在linux中,用户密码是以散列值形式存储在文件/etc/shadow中的。如下:
mapgoo:$6$DrhyZgNO$RlO/8BMN5h65YiVXACy78OdhJD3XqHb.t4q7yih24YA3HYgLOkmvbBIhZGHzfQJvBfuvTZAV6vserCCJ.GDpa/:16501:0:99999:7::: 
  该行中的$6$DrhyZgNO$RlO/8BMN5h65YiVXACy78OdhJD3XqHb.t4q7yih24YA3HYgLOkmvbBIhZGHzfQJvBfuvTZAV6vserCCJ.GDpa/ 是密码对应的shadow散列值。
   下面我们看看如何用openssl生成shadow密码。 shadow密码通常都是salt密码。所谓salt就是额外 的一个字符串,用来起一个混淆的作用,使加密更加不易被破解。salt由一些随机位组成,被用作密钥生成函数的输入之一,以生成密码的salt散列值。
  eg:  opensslpasswd -l -salt SALT_STRING  PASSWORD    //将SALT_STRING替换为随机字符串,并将PASSWORD替换成你想要使用的密码


7.排序,唯一与重复
7.1 sort
    eg:  sort file1.txt  file2.txt > sorted.txt  //对文件排序(等价于: sort file1.txt file2.txt -o   sorted.txt )
    eg:  sort  -n file.txt   //按照数字顺序进行排序
    eg:  sort -r file.txt     //按照逆序进行排序
    eg:  sort  -M  months.txt   //按照月份进行排序 (依照一月,二月。。。)
    eg: sort -m sorted1  sorted2    //合并两个排序过的文件
    eg:  sort  file1.txt     file2.txt    |  uniq     //找出已排序文件中不重复的行
    eg:   找出文件是否已经排序过
  #!/bin/bash   
 sort -C   filename    //如果文件已经排序,sort会返回为0的退出码,否则返回非0
 if [ $? -eq 0 ] ; then   
 echo sorted;
 else
echo Unsorted;
fi                                               

7.2 依据键或列进行排序
eg:  sort -nrk 3  data.txt   //依据第一列,以逆序形式排序  (-k指定了排序应该按照哪一个键key来进行。键指的是列号,而列号就是执行排序时的依据。)
排序前: 1  mac     2000
               2  winxp   4000
               3   linux    100

排序后:      2  winxp   4000
               
  1  mac     2000
                 3   linux    100
               
                

eg: sort -nk 3,4 data.txt   //起始位置是3和4
(通常情况下,键就是文本中的列。列与列之间用空格分隔。但有时候,我们需要将特定范围内的一组字符作为键。在这种情况下,必须明确地将键指定为某个范围内的字符。这个范围可以用键起止的字符位置来表明。)
 排序前: 
1010hellothis
                2189ababbba
                7464dfddfdfd
                3265fdcgbg
 
排序后:    1010hellothis
                 2189ababbba
                 3265fdcgbg
                 7464dfddfdfd

 eg: sort -bd unsorted.txt    //忽略多余空格字符,并以字典序进行排序 ( -b用于忽略文件中的前导空白行; -d 用于指明以字典序进行排序。) 

eg: sort -z data.txt | xargs -0   //为使sort的输出与以\0作为终止符的xargs命令兼容,采用加-z选项。(终止符\0用来保证xargs命令的使用安全)

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

eg: uniq sorted.txt   //打印出给定的输入数据中生成唯一的行 (所谓“唯一的行”是指来自输入的所有行都会被打印出来,但是其中重复的行只会被打印一次) 
eg: sort  Unsorted.txt | uniq

eg:  uniq -u sorted.txt  //只显示唯一的行(在输入文件中没有重复出现的行。  注意与上面的区别)

eg:  uniq -d  sorted.txt   //找出文件中重复的行 (区别于上面的)

eg: uniq -c    sorted.txt   //统计各行在文件中出现的次数

eg: sort data.txt | uniq -s 2 -w 2  //使用醒目的字符作为唯一键。(-s 指定可以跳过前n个字符; -w 指定用于比较的最大字符数。)
 操作前: u:01:gnu
               d:04:linux
               u:01:bash
               u:01:hack

 操作后: d:04:linux
               u:01:bash

eg: uniq -z  file.txt  //用uniq命令生成包含0值字符终止符的输出 (在与xargs命令一同使用时,xargs命令会用空格作为默认定界符分隔参数。)

eg:uniq -z file.txt | xargs -0 rm  //  删除所有指定的文件,这些文件的名字是从file.txt中读取的(如果某个文件名在文件中出现多次,uniq命令只会将这个文件名写入stdout一次)

 
9.临时文件命名与随机数
 编写shell脚本时,我们经常需要存储临时数据。最适合存储临时数据的位置是/tmp(该目录中的内容在系统重启后会被清空。)
9.1   eg: filename='mktemp'  //创建临时文件  ( /tmp/tmp.8xvhkjf5fH )
 
9.2 eg:  dirname='mktemp -d'   //创建临时目录

9.3 eg:  tmpfile='mktemp -u'   //仅仅生成文件名,不会创建实际的文件或目录

9.4 eg: mktemp test.XXX    //根据目标创建临时文件名(X会被随机的字符替换,注意,mktemp正常工作的前提是保证模板中至少要有3个X。)

10.分隔文件和数据
10.1 split(只能根据大小或行数分割文件)
eg: split -b 10k data.file    //将文件data.file分割成10K大小的文件。(文件名以: xaa  xab  xac  xad xae。。。表示)
eg: split -b 10k  data.file -d -a  4   //-d表示以数字为后缀; -a 表示指定后缀的长度  (除了k(KB)后缀,还有M(MB),G(GB),c(byte),w(word)。)

eg: split -b 10K  data.file -d -a 4  split_file  //生成前缀为split_file的文件;如split_file0001,split_file0002,...  (split命令最后一个参数是前缀PREFIX,格式:split  [COMMAND_ARGS] PREFIX  )

eg: split -l 10  data.file  //分割成每个文件为10行的文件  (按行分割)


10.2  csplit (split工具的一个变体,可以根据文本自身的特点进行分割。是否存在某个单词或文本内容都可以作为分割文件的条件)
  eg:   csplit  server.log  /SERVER/  -n 2 -s {*}  -f  server -b "%02d.log" ; rm server00.log   //server.log文件分割成server01.log,server02.log,。。。 
 ( /SERVER/ :用来匹配某一行,分割过程即从此刻开始。 /[REGEX]/ 表示文本样式。包括从当前行(第一行)直到(但不包括)包含“SERVER"的匹配行;
      {*} :表示根据匹配重复执行分割,直到文件末尾为止,可以用 {整数} 的形式来指定分割执行的次数;
      -s :使命令进入静默模式,不打印其他信息;
     -n : 指定分割后的文件名后缀的数字个数,例如01,02,03等;
     -f  :指定分割后的文件名前缀;
     -b : 指定后缀格式。例如%02d.log,类似于C语言中的printf的参数格式;
     因为分割后的第一个文件没有任何内容(匹配的单词就位于文件的第一行中),所以我们删除了server00.log。)

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

 eg:
file_jpg="sample.jpg"
name=${file_jpg%.*}      //提取文件名(借助%操作符)
extension=${file_jpg#*.} //提取扩展名(借助#操作符)
echo File name is: $name     //结果: File name is: sample
echo Extension is: $extension  //结果: Extension is: jpg

%操作符:提取文件名
${VAR%.*} 的含义如下:从$VAR中删除位于%右侧的通配符(在上例中是.*)所匹配的字符串。通配符从右向左进行匹配。
                                       给VAR赋值,VAR=sample.jpg,那么,通配符从右向左就会匹配到.jpg,因此从$VAR中删除匹配结果,就会得到输出sample。

%属于非贪婪(non-greedy)操作。它从右到左找到匹配通配符的最短结果。
%%属于贪婪操作。意味着它会匹配符合条件的最长字符串。
eg: VAR=hack.fun.book.txt
         echo  ${VAR%.*}   //结果为: hack.fun.book
         echo  ${VAR%%.*}   //结果为: hack

#操作符:提取扩展名
 ${VAR#*.} 的含义: 从$VAR中删除位于#右侧的通配符(即在上例中使用的*.)所匹配的字符串。通配符从左向右进行匹配。

#属于非贪婪操作。
##属于贪婪操作。
eg:VAR=hack.fun.book.txt
        echo  ${VAR#*.}   //结果为:fun.book.txt
        echo ${VAR##*.}  //结果为:txt

11.2eg:  URL="www.google.com"
                 echo ${URL%.*}    //  www.google
                 echo ${URL%%.*}  // www
                 echo ${URL#*.}     //google.com
                  echo ${URL##*.}  //com

12.批量重命名和移动
eg:用特定的格式重命名当前目录下的图像文件
--------------------------------------------------------
#!/bin/bash
#文件名:rename.sh
#用途:重命名.jpg和.png文件
count=1;
for img in 'find . -iname '*.jpg' -o -iname '*.png' -type f -maxdepth 1'
do
     new=image-$count.${img##*.}
     echo "Renaming $img to $new"
     mv   "$img" "$new"
     let count++
done
-----------------------------------------------------
得到的文件名:image-1.jpg;image-2.jpg;image-3.png;。。。

其他例子:
12.2将*.JPG更名为.jpg
rename *.JPG *.jpg
12.3将文件名中的空格替换成字符"_"
rename 's/ /_g' *  
12.4转换文件名的大小写
rename 'y/A-Z/a-z/' *
rename 'y/a-z/A-Z' *
12.5将所有的.mp3文件移入给点的目录
find path -name "*.mp3" -type f -exec mv {} target_dir \;
12.6将所有文件名中的空格替换为字符"_"
find path -type f -exec rename 's/ /_g' {} \;


13.交互输入自动化
eg: 读取交互式输入脚本
-------------------------------
#!/bin/bash
#文件名: interactive.sh
read -p "Enter number:"  no;
read -p  "Enter name:" name
echo You have entered $no, $name;
------------------------------
echo  -e   "1\nhello\n" | ./interactive.sh   
运行结果:You have entered 1,hello


 echo -e 来生成输入序列,-e表明echo会解释转义序列。如果输入内容较多,那么可以用单独的输入文件结合重定向操作符来输入:
echo -e "1\nhello\n" > input.data
./interactive.sh < input.data

13.2 expect 实现自动化(该命令需要手动安装)
eg:通过检查输入提示来发送数据
-------------------------------------
#!/usr/bin/expect
#文件名:automate_expect.sh
spawn ./interactive.sh     //spawn参数指定需要自动化哪一个命令
expect "Enter number:"   //expect参数提供需要等待的消息
send "1\n"                       //send是要发送的消息
expect "Enter name:"
send "hello\n"
expect eof                      //expect eof指明命令交互结束
---------------------

14利用并行进程加速命令执行
不是cpu时钟频率变高,而是多核的出现。单个物理处理器中包含多个逻辑处理器。

eg: 以md5sum命令为例。由于涉及运算,该命令属于cpu密集型命令。如果多个文件需要生成校验和
----------------------
#!/bin/bash
#文件名:generate_checksums.sh
PIDARRY=()
for file in   File1.iso    File2.iso
do
  md5sum $file &                       // 
  PIDARRAY+=("$!")                  //
done
wait $(PIDARRAY[@]}
----------------------
上面运行的结果与这个一样(md5sum File1.iso  File2.iso ),只是更快的获得运行结果。

工作原理:& 使命令放置于后台并继续运行,不过意味着一旦循环结束,脚本就会退出,而md5sum命令仍在后台运行。为了避免这种情况,我们使用$!来获得进程的PID,将这些PID放在数组,然后使用wait命令等待这些进程结束。

你可能感兴趣的:(SHELL笔记(二))