Shell脚本进阶

一、显示数据

1.1 理解输入和输出

  1. 标准文件描述符
    • 0 STDIN 标准输入
      • 就是从键盘(通常是/dev/tty)获取数据。
      • 使用重定向时,会使用重定向指定的文件来替换STDIN
    • 1 STDOUT 标准输出
      • 就是输出到终端显示器(一般是/dev/tty)
      • 使用重定向时,可以将输出到指定的文件中。
    • 2 STDERR 标准错误
      • 运行的程序和脚本出错时生成的错误消息都会发送到STDERR
      • 默认情况下,STDERR也是输出到终端显示器
      • 使用2>重定向,例如ls -al 不存在的文件 2> 收集错误的文件
      • 同时重定向STDERR和STDOUT
        • ls -al 不存在的文件 存在的文件 2> 收集错误的文件 1>正确的文件
        • 也可以ls -al 不存在的文件 存在的文件 &>同一个文件,错误的内容会放在前面。

1.2 在脚本中重定向输出

  1. 临时重定向
    • 将STDOUT重定向到STDERRecho "this is error" >&2
      • 原本是输出到STDOUT,现在输出到STDERR
  2. 永久重定向
    • exec 1>out.log,所有的STDOUT都重定向到out.log中。
    • exec 2>err.log,所有的STDERR都重定向到err.log中

1.3 在脚本中重定向输入

  1. exec 0< file,接下来通过STDIN获取数据,都会通过file来获取。

1.4 创建重定向

  1. 创建输出文件描述符exec 3>test3out

  2. 重定向文件描述符

    • 例子

      #下面是脚本内容
      $ cat test14
      #!/bin/bash
      exec 3>&1 # 首先 创建了文件描述符3,然后指向1指向的位置
      exec 1>testout # 然后1指向的位置改成testout文件
      # 因为echo默认都是输出到1,所以现在输出到了testout文件
      echo "This should store in the output file"
      echo "along with this line."
      # 然后把1重新指向3指向的,也就是终端显示器
      exec 1>&3
      # echo输出到1,也就是终端显示器了。
      echo "Now things should be back to normal"
      # 下面是执行脚本的结果
      $ ./test14
      Now things should be back to normal
      $ cat testout
      This should store in the output file
      along with this line.
      $
    • 文件描述符就可以看作是指针,指向一个区域。

  3. 创建输入文件描述符

    • 例子
      #!/bin/bash
      # 先将原来从键盘输入保存起来
      exec 6<&0
      # 将STDIN指向testfile文件
      exec 0< testfile
      count=1
      while read line
      do
      echo "Line #$count: $line"
      count=$[ $count + 1 ]
      done
      # 将STDIN指回键盘输入
      exec 0<&6
      read -p "Are you done now? " answer
      case $answer in
      Y|y) echo "Goodbye";;
      N|n) echo "Sorry, this is the end.";;
      esac
  4. 创建读写文件描述符

    • 例子

      $ cat test16
      #!/bin/bash
      #创建文件描述符 可以同时读写
      exec 3<> testfile
      # 从文件中读取一行数据
      read line <&3
      # 输出到STDOUT,也就是终端显示器上
      echo "Read: $line"
      # 输出到文件描述符3,也就是testfile中
      echo "This is a test line" >&3
      # 查看原来的testfile文件内容
      $ cat testfile
      This is the first line.
      This is the second line.
      This is the third line.
      # 执行脚本
      $ ./test16
      Read: This is the first line.
      # 查看现在的testfile文件
      $ cat testfile
      This is the first line.
      This is a test line
      ine.
      This is the third line.
      $
    • 注意到,testfile文件的一部分被修改了,是因为读取文件时,有一个指针指向了第二行数据。然后又写,所以把第二行数据的一部分给覆盖了。

  5. 关闭文件描述符

    • 语法exec 3>&-
    • 关闭了之后,就不能读写了。如果再创建一个文件描述符,指向同一个文件,此时会新创建一个文件,覆盖原有的文件

1.5 列出打开的文件描述符lsof

  1. -p指定进程ID

  2. -d指定要先的文件描述符编号

  3. -a对其他两个选项的结果执行AND运算

  4. 例子:lsof -a -p $$ -d 0,1,2

    • $$表示当前shell的进程ID

    • 结果

      COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
      bash 5474 tom 0u CHR 136,1 0t0 4 /dev/pts/1
      bash 5474 tom 1u CHR 136,1 0t0 4 /dev/pts/1
      bash 5474 tom 2u CHR 136,1 0t0 4 /dev/pts/1
      • COMMAND: 正在运行的命令名的前9个字符
      • PID:进程PID
      • USER:进程属主的登录名
      • FD:文件描述符号以及访问类型(r读、w写、u读写)
      • TYPE:文件的类型(CHR字符型,BLK块型,DIR目录,REG常规文件)
      • DEVICE:设备的设备号(主设备和从设备号)
      • SIZE/OFF:如果有,则代表文件的大小
      • NONE:本地文件的节点号
      • NAME:文件名

1.6 阻止命令输出

  1. 可以将输出重定向到/dev/null文件中,输出到null文件的任何数据都不会保存。
  2. 还可以cat /dev/null > file,将文件内容清空。

1.7 记录消息tee命令

  1. 同时将输出发送到显示器和日志文件命令 | tee 日志文件
    • 会覆盖文件
    • -a追加文件

1.8 使用例子

  1. 读取.csv数据,然后生成插入数据库的语句

    • #!/bin/bash
      # read file and create INSERT statements for MySQL
      outfile='members.sql'
      IFS=','
      while read lname fname address city state zip
      do
      # 这里是关键
      # cat的结果输入到文件中
      # 后面又使用EOF作为文本的结束标志,也就是使用后面一大坨的内容作为cat的输入
      cat >> $outfile << EOF
      INSERT INTO members (lname,fname,address,city,state,zip) VALUES
      ('$lname', '$fname', '$address', '$city', '$state', '$zip');
      EOF
      done < ${1} # 注意这里 表示read的输入重定向为脚本的第一个参数,也就是数据文件

二、控制脚本

2.1 处理信号

  1. Linux信号

    • 1 SIGHUP 挂起进程
    • 2 SIGINT 终止进程
    • 3 SIGQUIT 停止进程
    • 9 SIGKILL 无条件终止进程
    • 15 SIGTERM 尽可能终止进程
    • 17 SIGSTOP 无条件停止进程,但不是终止进程
    • 18 SIGTSTP 停止或暂停进程,但不终止进程
    • 19 SIGCONT 继续运行停止的进程
  2. 生成信号

    • 中断进程Ctrl + C,生成SIGINT信号
    • 暂停进程Ctrl + Z,生成SIGTSTP信号
      • 在前台继续运行fg %作业号,使用jobs查看作业号
      • 在后台继续运行bg %作业号
  3. 捕获信号

    • 作用:如果脚本收到了trap命令中列出的信号,该信号不再由shell处理,而是交由本地处理。

    • 语法trap 命令 信号

    • 例子:

      #/bin/bash
      # 打印100~0
      COUNT=100
      # 收到SIGINT时,直接打印这句话
      trap "echo 'sorry i can continue..'" SIGINT
      while [ $COUNT -ge 0 ]
      do
      echo "current is $COUNT"
      COUNT=$[$COUNT - 1]
      sleep 1
      done
    • 捕获脚本退出,也就是脚本退出时,执行操作trap "echo Goodbye..." EXIT

    • 修改信号,只需要重写一遍即可,即捕获相同的信号,后者会覆盖前者。

    • 删除信号trap -- SIGINT

2.2 后台运行脚本

  1. 运行脚本时,在命令末尾加一个&,例如sleep 100&
    • 然后shell会输出信息[3] 52261,方框代表的是作业号,后面的数字是进程号
    • 后台作业仍然会使用当前shell的STDIN、STDOUT、STDERR
    • 后台进程和当前shell联系在一起,如果当前shell退出,所有的后台进程结束。
  2. 在非控制台下运行脚本
    • nohub 脚本 &
    • 如果关闭会话,脚本会忽略终端会话发来的SIGHUP信号。
    • nohup命令会将STDOUT和STDERR消息重定向到nohup.out文件中。

2.3 作业控制

  1. 查看作业:jobs
    • -l列出进程PID和作业号
    • -r只列出运行中的作业
    • -s只列出已停止的作业
    • 列出的作业有+和-,+被当作默认作业,在使用作业控制命令时,如果未在命令行指定任何作业号,该作业被当作对象。-被当作下一个默认作业。同一时刻只有一个+和一个-
  2. 重启停止的作业
    • bg %作业号后台继续运行
    • fg %作业号前台继续运行

2.4 调整优先级

  1. 从+20(最高优先级)到+19(最低优先级)。默认情况下,bash shell以优先级0来启动所有进程。
  2. nice命令:nice -n 10 脚本,以10的优先级运行脚本
    • 如果要调整到0更高的优先级,需要有管理员权限。
    • 可以使用ps -p 进程号 -o ni来查看进程的优先级
  3. renice命令:改变系统上已运行命令的优先级
    • renice -n 优先级 -p pid
    • 只能对所属的进程执行renice
    • 只能通过renice降低进程的优先级

2.5 定时运行作业

  1. at命令
    • at命令会将作业提交到队列,at的守护进程atd以后台模式运行,检查作业队列来运行作业。
    • atd守护进程会检查系统的一个特殊目录/var/spool/at来获取at命令提交的作业,默认情况下,每隔60秒检查一次。
    • 格式at -f 脚本 时间
    • at命令利用sendmail应用程序发送邮件。因此,最好将STDOUTSTDERR重定向。
    • atq列出有哪些作业在等待。
    • atq 作业号删除作业。
posted @   ouyangxx  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示