《linux命令行与shell编程大全》--读书笔记

大憨熊 提交于 2020-11-26 09:52:13

1.初识Linux shell

linux
图片链接


2.走进shell

1.进入命令行,CLI(command line interface),也叫Linux控制台

  • 通过Linux控制台终端访问CLI
  • Ctrl+Alt+F1~F7
    注:tty:teletypewriter,指一台用于发送消息的机器
    控制台的外观设置命令:setterm

2.通过图形化的终端访问CLI


3.基本的bash shell命令

1. bash手册

1.1 熟悉DESCRIPTION部分的前两段可以学到很多技术行话
1.2 空格键:翻页
1.3 回车键:逐行查看

2. Linux手册页的内容区域

  • 1.可执行文件或shell命令
  • 2.系统调用
  • 3.库调用
  • 4.特殊文件
  • 5.文格式约定
  • 6.游戏
  • 7.概览、约定及杂项
  • 8.超级用户和系统管理员命令
  • 9.内核例程
  • 查看所需的页面:man section# topic
  • 查看内容简介:man 1 intro
  • 另一个参考信息:info info

3.Linux文件系统

3.1 常见的目录名称
  • / :虚拟目录的根目录
  • /bin :二进制目录,存放许多用户级的GNU工具
  • /boot:启动目录,存放启动文件
  • /dev :设备目录,Linux在这里创建设备节点
  • /etc :系统配置文件目录
  • /home:主目录,Linux在这里创建用户目录
  • /lib :库目录,存放系统和应用程序的库文件
  • /media:媒体目录,可移动媒体设备的常用挂载点
  • /mnt :挂载目录,另一个可移动媒体设备的常用挂载点
  • /opt :可选目录,常用于存放第三方软件包和数据文件
  • /proc:进程目录,存放现有硬件及当前进程的相关信息
  • /root:root用户的主目录
  • /sbin:系统二进制目录,存放许多GNU管理员级工具
  • /run :运行目录,存放系统运作时的运行时数据
  • /srv :服务目录,存放本地服务的相关文件
  • /sys :系统目录,存放系统硬件信息的相关文件
  • /tmp :临时目录,可以在该目录中创建和删除临时工作文件
  • /usr :用户二进制目录,大量用户级的GNU工具和数据文件都存储在这里
  • /var :可变目录,用以存放经常变化的文件,比如日志文件
3.2 文件和目录列表
  • ls -F :区分文件和目录
  • ls -a :显示隐藏目录
  • ls -F -R :递归列出当前目录下包含的子目录中的文件
  • ls -l :长列表格式输出,每行都包含了下述信息
     文件类型,比如目录( d )、文件( - )、字符型文件( c )或块设备( b );
     文件的权限(参见第6章);
     文件的硬链接总数;
     文件属主的用户名;
     文件属组的组名;
     文件的大小(以字节为单位);
     文件的上次修改时间;
     文件名或目录名;
3.3 使用元字符通配符过滤文件
  • ls -l scr[ai]pt :a或i
  • ls -l f[a-i]ll :指定字符范围
  • ls -l f[!a]ll :除了a
3.4 创建文件
  • touch test_one :创建一个空文件,如果已经存在,则修改文件的创建时间
  • touch -a test_one :改变文件创建时间
  • ls -l –time=atime test_one :加粗文件创建时间
3.5 链接文件:指向文件真实位置的占位符
3.5.1 符号链接:指向虚拟结构目录中某个地方的另一个文件,彼此内容不同
  • ln -s data_file s1_data_file :给data_file创建一个符号链接
  • ls -l *data_file :查看该链接文件,可以看到大小不一样
  • ls -i *data_file :查看两个文件的inode节点编号,可以看到不一样
3.5.2硬链接:会创建独立的虚拟文件,包含原始文件的信息和位置,本质上是同一个文件(必须在同一个媒体)
  • ln code_file h1_code_file :创建一个硬链接
  • ls -li *code_file :查看两个文件
3.6.删除文件
  • rm -i fall :带提示的删除
  • rm -f fall :强制删除
  • rm -ri my_diir:删除目录中的文件,再删除目录本身
  • rm -ri my_diir:递归删除目录中的文件,再删除目录本身
3.7 创建目录
  • mkdir -p New_dir/Sub_dir/under_ir :同时创建多个目录及子目录用 -p参数
  • tree New_dir :以树的形式查看目录结构
3.8 查看文件内容
  • file my_file :查看文件或者目录类型
  • cat -n test1 :显示文本文件数据,并加上行号
  • cat -b test1 :只给有文本的加上行号
  • cat -T test1 :不让制表符出现
  • more test1 :显示文件内容,不过会在每一页后停下来
  • less test1 :跟more差不多,但是更高级,支持方向键上下翻页
  • tail -n 2 test1 :只显示最后两行的内容
  • head -n 2 test1 :显示文件开头两行内容

4. 更多的bash shell命令

1.监测程序
  • ps :显示运行在当前控制台下的属于当前用户的进程
  • ps -ef :显示所有进程的完整信息
    其他参数
  • -A 显示所有进程
  • -N 显示与指定参数不符的所有进程
  • -a 显示除控制进程(session leader ① )和无终端进程外的所有进程
  • -d 显示除控制进程外的所有进程
  • -e 显示所有进程
  • -C cmdlist 显示包含在 cmdlist 列表中的进程
  • -G grplist 显示组ID在 grplist 列表中的进程
  • -U userlist 显示属主的用户ID在 userlist 列表中的进程
  • -g grplist 显示会话或组ID在 grplist 列表中的进程 ②
  • -p pidlist 显示PID在 pidlist 列表中的进程
  • -s sesslist 显示会话ID在 sesslist 列表中的进程
  • -t ttylist 显示终端ID在 ttylist 列表中的进程
  • -u userlist 显示有效用户ID在 userlist 列表中的进程
  • -O format 显示默认的输出列以及 format 列表指定的特定列
  • -o format 仅显示由 format 指定的列
  • -n namelist 定义了 WCHAN 列显示的值
  • -F 显示更多额外输出(相对 -f 参数而言)
  • -M 显示进程的安全信息
  • -c 显示进程的额外调度器信息
  • -f 显示完整格式的输出
  • -j 显示任务信息
  • -l 显示长列表
    F:内核分配给进程的系统标记
    S:进程状态(O表示正在运行,S表示正在休眠,R表示可运行,正在等待运行,Z表示僵尸进程)
  • -y 不要显示进程标记(process flag,表明进程状态的标记)
  • -Z 显示安全标签(security context) ① 信息
  • -H 用层级格式来显示进程(树状,用来显示父进程)
  • -w 采用宽输出模式,不限宽度显示
  • -L 显示进程中的线程
  • -V 显示 ps 命令的版本号
top :跟ps相似,不过它显示的是实时的
2.结束进程
  • kill 3904 :给进程ID是3904的进程发送TERM(尽可能终止)信号
  • kill -s HUP 3940 :给进程ID是3904的进程发送HUP(挂起)信号
  • killall http* :结束以http开头的进程
3.检测磁盘空间
  • mount :列出当前系统上挂载的设备列表
  • mount -t vfat /dev/sdb1 /media/disk :手动将U盘/dev/sdb1挂载到/media/disk目录下
  • umount /home/rich/mnt :卸载设备
  • df -h :查看所有已挂载设备的的磁盘使用情况,-h用易读的方式显示
  • du /mnt/hgfs :查看特定目录下的磁盘使用情况,不加参数表示当前目录
4.处理数据文件
  • sort -n file :排序file文件,并把数字识别成数字
  • sort -M file3 :按月份(3个字母)排序,常用于日志文件
  • sort -t ‘:’ -k 3 -n /etc/passwd :对密码文件根据用户ID排序,-t指定区分键位置字符,-k排序其实位置
  • grep three file :在file中搜索three文本
  • grep -v three filre :在file文件中反向搜索three文本,即不包含three的行
  • grep -C three filre :在file文件中反向搜索three文本,只输出有多少个行含有匹配模式
  • grep -n three filre :在file文件中反向搜索three文本,仅输出行号
  • grep -e t -e f file :如果要指定多个模式,-e指定每个模式
  • grep [tf] file :使用正则表达式搜索包含字符t或者f的匹配
  • gzip my* :通过通配符一次性批量压缩文件
  • gzcat myprog.c.gz :查看压缩过的文本文件的内容
  • gunzip myprog.c.gz :解压文件
  • tar -cvf test.tar test/ test2/ :归档文件
  • tar -tf test.tar :列出test.tar的内容
  • tar -zxvf file.tgz :解压用gzip压缩过的文件

5.理解shell

shell不单单是一种CLI,它是一个时刻都在运行的交互式程序

/bin/sh:用于那些在系统启动时使用的系统shell脚本
ps –forest:展示进程间的嵌套结构

5.1 可通过命令行参数修改shell的启动方式
  • -c string 从 string 中读取命令并进行处理
  • -i 启动一个能够接收用户输入的交互shell
  • -l 以登录shell的形式启动
  • -r 启动一个受限shell,用户会被限制在默认目录中
  • -s 从标准输入中读取命令
5.2 常用命令
  • echo $BASH_SUBSHELL :查看子shell的个数
  • sleep 10 :会话暂停10秒钟
  • sleep 10& :sleep命令睡眠10秒钟放到后台运行
  • jobs :显示当前运行在后台模式的所有用户进程(作业)
  • coproc sleep 10 :在后天生成一个子shell,并把命令放到后台运行
  • coproc My_job { sleep 10; } :给协程命名
5.3 理解shell的内建命令
5.3.1 外部命令:也叫文件系统命令,通常位于/bin、/usr/bin、/sbin或者/usr/sbin中
  • which ps :用which命令找到外部ps命令
  • type ps :跟上面一样
5.3.2内部命令:不需要使用子进程来执行,作为shell的组成部分存在
  • type cd :查看cd是不是内部命令
  • history :跟踪使用过的命令,保存在隐藏文件.bash_history中,位于用户主目录
    !! :唤回并重用历史命令中最近的命令
    history -a :强制将历史记录写入.bash_history
    !20 :唤回历史命令中的第20条命令
  • alias :查看可用的别名
  • alias ll=’ls -alF’ :设置别名,不过仅在被定义的shell中才有效

6.使用Linux环境变量

bash shell用一个叫做环境变量的(enviroment variable)的特性来存储有关 shell会话工作环境 的信息
6.1 全局/局部 环境变量
—-全局环境变量对于shell会话和所有生成的字shell都是可见的
—-局部变量则只对创建他们的shell可见
+ printenv :查看所有全局变量
+ printenv HOME :查看单个环境变量
+ env HOME :查看单个全局变量
+ echo HOME:+lsHOME:查看单个全局变量+lsHOME :通过"$”引用HOME的值
+ set :显示所有全局、局部和用户自定义的环境变量

6.2设置用户定义变量
+ my_variable=hello :变量名、等号和值之间没有空格,在子shell中不能用,退出shell后也会被清除
+ echo variable:+exportmyvariable:shellshell使export+unsetmyvariable:+myprog:+exportPATH=variable:查看变量值+exportmyvariable:将变量导入到全局环境,修改子shell环境变量中的值并不会改变父shell中该变量的值,使用export也不能+unsetmyvariable:删除环境变量,不过在子进程中删除,在父进程中依然有用+myprog:打印系统消息+exportPATH=PATH:. #修改环境变量,持续到退出或重启系统

6.3 定位系统环境变量
当登入Linux系统启动一个bash shell时,默认情况下bash会在几个文件中查找命令,这些文件叫 启动文件环境文件

6.3.1 登录shell
  • 登录linux系统时,bash shell会作为默认shell启动,登录shell会从5个不同的启动文件里读取命令
     /etc/profile :主启动文件,每个用户登录都会执行
    HOME/.bashprofileHOME/.bashprofile:按顺序运行下面三个文件中的第一个文件,其他的被忽略HOME/.bashrc:’.’开头的一般是隐藏文件,这个文件通常通过其他文件运行
    HOME/.bashloginHOME/.bashloginHOME/.profile
6.3.2 交互式shell进程(CLI中敲bash)
  • 只检查HOME目录下的.bashrc文件
    • 查看、/etc目录下通用的bashrc文件
    • 为用户提供一个定制自己的命令别名和私有脚本函数的地方

6.3.3 非交互式shell (执行shell脚本时使用)
__6.4 环境变量的持久化__ + 放入/etc/profile:升级后就没有了 + 在/etc/profile.d目录中创建一个.sh脚本:这是最好的方法 + 放入 HOME/.bashrc:存储个人永久变量的最好方法

__6.5 数组变量__
mytest=(one two three four five)  :值放括号,值之间用空格隔开
echo
HOME/.bashrc:存储个人永久变量的最好方法__6.5 数组变量__mytest=(one two three four five)  :值放括号,值之间用空格隔开echo
{mytest[2]} :引用单独的数组元素,索引值要用方括号 echo ${mytest[*]} :显示整个数组元素 mytest[2]=severn :可以改变索引位置的值 unset mytest[2] :删除相当于mytest[2]=””

7.理解Linux文件权限

linux安全系统的核心是用户账户。用户权限是通过创建用户时分配的用户ID(缩写为UID) __7.1 /etc/passwd文件__ + __系统账户__:系统上运行的各种服务进程访问资源用的特殊账户,所有运行在后台的服务都需要用一个系统用户账户登录到Linux系统上 + 用户密码被设置成了x,这是因为保存到了 __/etc/shadow__ 文件 + __/usr/sbin/useradd hzq -m -p h__ :创建一个用户hzq,密码是h,如果不加选项,会按照/etc/default/useradd文件默认配置,可以-D选项查看,加上-m后会创建home目录并将 **/etc/skel** 目录中的文件复制过来 + **userdel -r hzq** :删除账户,同时删除/home目录下的hzq文件夹 + **usermod hzq -p 123** :修改用户密码 + **passwd hzq** :修改用户密码或用来修改当前用户密码 + chpasswd
7.2.1 /etc/group 文件
  • 共有四个字段:组名、组密码、GID、属于该组的用户列表(空表示在/etc/passwd中指定了默认组)
  • 系统账户的GID低于500,用户的组ID从500/1000以后开始分配
  • /usr/sbin/groupadd shared :创建组
  • usermod -G shared hzq :将用户添加到组,重新登录后关系生效
  • groupmod -n sharing shared :修改组名与GID

7.3 理解文件权限

ls -l 输出的第一个字符代表了对象的类型

 - 代表文件
 d 代表目录
 l 代表链接
 c 代表字符型设备
 b 代表块设备
 n 代表网络设备

ls -l 第一个字段之后有3组三字符的编码。每一组定义了3种访问权限(若没有某种权限,在该权限位会出现单破折线):

 r 代表对象是可读的
 w 代表对象是可写的
 x 代表对象是可执行的

这3组权限分别对应对象的3个安全级别

 对象的属主
 对象的属组
 系统其他用户
用户权限.png
+ umask 026 :用 umask 命令为默认 umask 设置指定一个新值,同样会作用在创建目录上,见/etc/login.defs或/etc/profile

7.4 改变安全性设置
+ chmod 760 newfile :八进制模式设置
+ chmod o+r newfile :符号模式,[ugoa…][[+-=][rwxXstugo…]

 u 代表用户
 g 代表组
 o 代表其他
 a 代表上述所有
 X :如果对象是目录或者它已有执行权限,赋予执行权限。
 s :运行时重新设置UID或GID。
 t :保留文件或目录。
 u :将权限设置为跟属主一样。
 g :将权限设置为跟属组一样。
 o :将权限设置为跟其他用户一样。
+ chown hzq.shared newdir -R:可用登录名或UID来指定文件的新属主.属组,递归地改变子目录和文件的所
属关系
+ chown hzq. newfile :如果你的Linux系统采用和用户登录名匹配的组名,可以只用一个条目就改变二者。
+ chgrp shared newfile :更改文件或目录的默认属组

7.5 共享文件
+ chmod g+s testdir :使目录里的新文件都能沿用目录的属组,只需将该目录的SGID位置位,等效于:chmod 2776 testdir
+ Linux还为每个文件和目录存储了3个额外的信息位。
 粘着位:进程结束后文件还驻留(粘着)在内存中。
 设置组ID(SGID):对文件来说,程序会以文件属组的权限运行;对目录来说,目录中创建的新文件会以目录的默认属组作为默认属组。
 设置用户ID(SUID):当文件被用户使用时,程序会以文件属主的权限运行。

8. 管理文件系统

8.1 日志文件系统

    1. ext4文件系统 : 在2008年受到Linux内核官方支持
      区段(extent)的特性:区段在存储设备上按块分配空间,但在索引节点表中只保存起始块的位置
      块预分配技术(block preallocation):ext4文件系统用 0 填满预留的数据块,不会将它们分配给其他文件
    1. Reiser文件系统:只支持回写日志模式——只把索引节点表数据写到日志文件—->最快的日志文件系统之一
      可以在线调整已有文件系统的大小—->最快的日志文件系统之一
      尾部压缩(tailpacking),该技术能将一个文件的数据填进另一个文件的数据块中的空白空间

8.2 写时复制文件系统(COW)

    1. ZFS文件系统
    1. Btrf文件系统

8.3 操作文件系统

。。。。。。。。。。

9. 安装软件程序

  • yum list installed : 找出系统上已安装的包
  • yum list updates :列出需要更新的
  • 从源码安装:
    1. tar -zxvf sysstat-11.1.1.tar.gz :解压
    2. cd sysstat-11.1.1 :进入解压目录
    3. ./configure :检查,确保它拥有合适的编译器能够编译源代码,另外还要具备正确的库依赖关系
    4. make :构建各种二进制文件
    5. make install :安装到Linux系统中常用的位置上

10. 使用编辑器

10.1 vim 编辑器

  • 10.1.1 普通模式
     h :左移一个字符。
     j :下移一行(文本中的下一行)。
     k :上移一行(文本中的上一行)。
     l :右移一个字符。
     PageDown (或Ctrl+F):下翻一屏。
     PageUp (或Ctrl+B):上翻一屏。
     G :移到缓冲区的最后一行。
     num G :移动到缓冲区中的第 num 行。
     gg :移到缓冲区的第一行。
     w filename :将文件保存到另一个文件中
    删除:x(取叉、错的意思):删除当前光标所在位置的字符
    删除:dd(delete ):删除当前光标所在行,相当于剪切
    删除:dw(delete word):删除当前光标所在位置的单词
    删除:d (delete(delete) 删除当前光标所在位置至行尾的内容
    拼接行:J(join ),删除当前光标所在行行尾的换行符
    撤销:u(unsure)
    追加数据 :a(add),类似于 “i”
    行尾追加 : A(Add)
    insert:r char(char表示一个字符):用 char 替换当前光标所在位置的单个字符
    insert:R text(相当于insert)直到按下ESC键
    复制:v—->移动光标—->y—->p
    查找:/+string—–>n(next)
    替换:s/old/new/
     :s/old/new/g :一行命令替换所有 old 。
     :n,ms/old/new/g :替换行号 n 和 m 之间所有 old 。
     :%s/old/new/g :替换整个文件中的所有 old 。
     :%s/old/new/gc :替换整个文件中的所有 old ,但在每次出现时提示。

10.2 nano 编辑器

10.3 emacs 编辑器

10.4 KWrite 编辑器

10.5 Kate(kdesdk) 编辑器

10.6 gedit编辑器

11. shell 脚本编程基础

11.1 显示消息

#!/bin/bash
# This script displays the date and who's logged on
echo -n The time and date are:                 #注意-n选项不换行
date
echo "Let's see who's logged into the system:" #所有的引号都可以正常输出了
who                                            #可以将 echo 语句添加到shell脚本中任何需要显示额外信息的地方

11.2 使用变量

#!/bin/bash
# display user information from the system.
echo "User info for userid: $USER"
echo UID: $UID                      #1.环境变量名称之前加上美元符"$"来使用这些环境变量
echo HOME: "$HOME"                  #2.加上双引号也没有问题
echo "The cost of the item is \$15" #3.想显示"$"符号需要在前面加上反斜杠
# testing variables
days=10                             #4.shell脚本会自动决定变量值的数据类型
guest="Katie"                       #5.变量名区分大小写
echo "$guest checked in $days days ago"
days=5                              #6.赋值时不用$,但是引用必须用$符号
guest="Jessica"
echo "$guest checked in $days days ago"
                                    #7.在shell脚本结束时变量会被删除掉
#有两种方法可以将命令输出赋给变量:
#1. 反引号字符( ` )  :注意不是单引号(')
#2. $() 格式                                   
one=`date`                          #8.shell命令的输出赋给变量方法1
two=$(date)                         #9.赋值等号和命令替换字符之间没有空格
echo "The date and time are: " $one #
#下面这个例子很常见,它在脚本中通过命令替换获得当前日期并用它来生成唯一文件名。
# copy the /usr/bin directory listing to a log file
today=$(date +%y%m%d)               #today存储日期
ls /usr/bin -al > log.$today        #ls的输出重定向到log.180515文件中,如果输出文件已经存在了,重定向操作符会用新的文件数据覆盖已有文件(默认的 umask 设置)

11.3 重定向输入和输出

  • 输入重定向符号是小于号( < )
    wc < test6 :wc 命令可以对对数据中的文本进行计数(行数、词数、字节数)
  • 内联输入重定向符号是远小于号(<<)
  • 管道:将一个命令的输出作为另一个命令的输入。这可以用重定向来实现
    rpm -qa | sort | more :排序rmp输出的软件包列表

11.4 执行数学运算

在shell脚本中有两种途径来进行数学运算
+ expr 1 + 5 :计算加法表达式
+ expr 5 * 2 :计算乘法表达式

ARG1 | ARG2
如果 ARG1 既不是null也不是零值,返回 ARG1 ;否则返回 ARG2
ARG1 & ARG2
如果没有参数是null或零值,返回 ARG1 ;否则返回 0
ARG1 < ARG2
如果 ARG1 小于 ARG2 ,返回 1 ;否则返回 0
ARG1 <= ARG2
如果 ARG1 小于或等于 ARG2 ,返回 1 ;否则返回 0
ARG1 = ARG2
如果 ARG1 等于 ARG2 ,返回 1 ;否则返回 0
ARG1 != ARG2
如果 ARG1 不等于 ARG2 ,返回 1 ;否则返回 0
ARG1 >= ARG2
如果 ARG1 大于或等于 ARG2 ,返回 1 ;否则返回 0
ARG1 > ARG2
如果 ARG1 大于 ARG2 ,返回 1 ;否则返回 0
ARG1 + ARG2
返回 ARG1 和 ARG2 的算术运算和
ARG1 - ARG2
返回 ARG1 和 ARG2 的算术运算差
ARG1 * ARG2
返回 ARG1 和 ARG2 的算术乘积
ARG1 / ARG2
返回 ARG1 被 ARG2 除的算术商
ARG1 % ARG2
返回 ARG1 被 ARG2 除的算术余数
STRING : REGEXP
如果 REGEXP 匹配到了 STRING 中的某个模式,返回该模式匹配
match STRING REGEXP
如果 REGEXP 匹配到了 STRING 中的某个模式,返回该模式匹配
substr STRING POS LENGTH
返回起始位置为 POS (从 1 开始计数)、长度为 LENGTH 个字符的子字符串
index STRING CHARS
返回在 STRING 中找到 CHARS 字符串的位置;否则,返回 0
length STRING
返回字符串 STRING 的数值长度
_ + TOKEN :将 TOKEN 解释成字符串,即使是个关键字
(EXPRESSION) : 返回 EXPRESSION 的值

  • var1=$[1 + 5] : 用美元符和方括号将数学表达式围起来
  • variable=$(echo “options; expression” | bc) :浮点解决方案之在脚本中使用 bc
#!/bin/bash
var1=100
var2=45
var3=$(echo "scale=4; $var1 / $var2" | bc)#将 scale 变量设置成了四位小数,并在 expression 部分指定了特定的运算
echo The answer for this is $var3
  • 浮点解决方案之最好的解决方案:使用内联输入重定向, 注意下面的注释:不能加Tab
#!/bin/bash
var1=10.23
var2=43.12
var3=33.2
var4=31
var5=$(bc<<EOF
scale = 4            #注意不能加tab键
a1 = ($var1 * $var2)
b1 = ($var3 * $var4)
a1 + b1
EOF
)
echo The final answer for this mess is $var5

11.5 退出脚本

  • 对于需要进行检查的命令,必须在其运行完毕后立刻查看或使用 $? 变量。它的值会变成由shell所执行的最后一条命令的退出状态码
  • Linux退出状态码

    0命令成功结束
    1 一般性未知错误
    2 不适合的shell命令
    126 命令不可执行
    127 没找到命令
    128 无效的退出参数
    128+x 与Linux信号x相关的严重错误
    130 通过Ctrl+C终止的命令
    255 正常范围之外的退出状态码

  • exit 命令:允许你在脚本结束时指定一个退出状态码
    如:exit 5

#!/bin/bash
#shell中运行的每个命令都使用退出状态码(exit status)告诉shell它已经运行完毕
var1=10
var2=30
var3=$[$var1 + $var2]
exit $var3            #exit 命令的参数中使用变量
                      #如果值大于255,返回模256后得到的余数

12. 使用结构化命令

12.1 使用if-then-fi语句

  • bash shell的 if 语句会运行 if 后面的那个命令。如果该命令的退出状态码(参见第11章)是 0(该命令成功运行),位于 then 部分的命令 才会 被执行
#!/bin/bash
# 这个脚本在 if 行采用了 pwd 命令。如果命令成功结束, echo 语句就会显示该文本字符串
if pwd
then
    echo "It worked"
fi
if IamNotaCommand       #由于这是个错误的命令,所以它会产生一个非零的退出状态码
then
    echo "It "
    echo "worked"       #这里可以放多条明令
else
    echo "It not worked!"
fi

12.2 if-then嵌套

#!/bin/bash
# 甚至可以更进一步,让脚本检查拥有目录的不存在用户以及没有拥有目录的不存在用户。这
# 可以通过在嵌套 elif 中加入一个 else 语句来实现。
testuser=NoSuchUser
#
if grep $testuser /etc/passwd
then
    echo "The user $testuser exists on this system."
#
elif ls -d /home/$testuser      #每块命令都会根据命令是否会返回退出状态码 0 来执行。记住,bash shell会依次执行 if 语句,
                                #只有第一个返回退出状态码 0 的语句中的 then 部分会被执行
then
    echo "The user $testuser does not exist on this system."
    echo "However, $testuser has a directory."
#
else
    echo "The user $testuser does not exist on this system."
    echo "And, $testuser does not have a directory."
fi

12.3 test 命令

  • 帮助通过 if-then 语句测试其他条件
  • 12.2.1 数值比较
    n1 -eq n2
    检查 n1 是否与 n2 相等
    n1 -ge n2
    检查 n1 是否大于或等于 n2
    n1 -gt n2
    检查 n1 是否大于 n2
    n1 -le n2
    检查 n1 是否小于或等于 n2
    n1 -lt n2
    检查 n1 是否小于 n2
    n1 -ne n2
    检查 n1 是否不等于 n2
#!/bin/bash
# Using numeric test evaluations
#使用中括号进行数值测试
value1=10
value2=11
#
if [ $value1 -gt 5 ]  #但是只能测试整数
then
    echo "The test value $value1 is greater than 5"
fi
#
if [ $value1 -eq $value2 ]
then
    echo "The values are equal"
else
    echo "The values are different"
fi
  • 12.3.2 字符串比较

    str1 = str2
    检查 str1 是否和 str2 相同
    str1 != str2
    检查 str1 是否和 str2 不同
    str1 < str2
    检查 str1 是否比 str2 小
    str1 > str2
    检查 str1 是否比 str2 大
    -n str1
    检查 str1 的长度是否非0
    -z str1
    检查 str1 的长度是否为0

  • 在比较测试中,大写字母被认为是小于小写字母的。但 sort 命令恰好相反,因为sort使用的是系统的本地化语言设置中定义的排序顺序,而比较测试中使用的是标准的ASCII顺序

  • -n(not zero) 和 -z (zoro)可以检查一个变量是否含有数据
#!/bin/bash
# mis-using string comparisons
#
val1=baseball
val2=hockey
#
if [ $val1 \> $val2 ]     #注意,必须加斜杠,否则会被当成重定向符
then
    echo "$val1 is greater than $val2"
else
    echo "$val1 is less than $val2"
fi

# testing string length
val1=testing
val2=''
#
if [ -n $val1 ]
then
echo "The string '$val1' is not empty"
else
echo "The string '$val1' is empty"
fi
#
if [ -z $val2 ]
then
echo "The string '$val2' is empty"
else
echo "The string '$val2' is not empty"
fi
#
if [ -z $val3 ]
then
echo "The string '$val3' is empty"
else
echo "The string '$val3' is not empty"
fi

12.3.3 文件比较

检查 file 是否存在并是一个目录 -d file
检查 file 是否存在 -e file
检查 file 是否存在并是一个文件 -f file
检查 file 是否存在并可读 -r file
检查 file 是否存在并非空 -s file
检查 file 是否存在并可写 -w file
检查 file 是否存在并可执行 -x file
检查 file 是否存在并属当前用户所有 -O file
检查 file 是否存在并且默认组与当前用户相同 -G file
检查 file1 是否比 file2 新 ,必须先确认文件是存在的 file1 -nt file2
检查 file1 是否比 file2 旧 file1 -ot file2
#!/bin/bash
# Look before you leap
# 文件测试
jump_directory=/home/hzq
#
if [ -d $jump_directory ]
then
    echo "The $jump_directory directory exists"
cd $jump_directory
ls
else
    echo "The $jump_directory directory does not exist"
fi

12.4 复合条件测试

if-then 语句允许你使用布尔逻辑来组合测试。有两种布尔运算符可用:
+ [ condition1 ] && [ condition2 ]
+ [ condition1 ] || [ condition2 ]

12.5 if-then 的高级特性

  • 用于数学表达式的双括号
#!/bin/bash
# using double parenthesis
#
val1=10
#
if (( $val1 ** 2 > 90 ))
then
(( val2 = $val1 ** 2 ))
echo "The square of $val1 is $val2"
fi
#   val++   后增
#   val--   后减
#   ++val   先增
#   --val   先减
#   !       逻辑求反
#   ~      位求反
#   **      幂运算
#   <<      左位移
#   >>      右位移
#   &       位布尔和
#   |       位布尔或
#   &&      逻辑和
#   ||      逻辑或
  • 用于高级字符串处理功能的双方括号:除了test提供的标准字符串比较,还提供的另一个特性——模式匹配
#!/bin/bash
# using pattern matching
# 使用模式匹配
if [[ $USER == r* ]]
then
echo "Hello $USER"
else
echo "Sorry, I do not know you"
fi

12.6 case命令

#!/bin/bash
# using the case command
# 使用case命令
case $USER in
rich | barbara)
    echo "Welcome, $USER"
    echo "Please enjoy your visit";;
testing)
    echo "Special testing account";;
jessica)  #")"相当于C语言中的":"
    echo "Do not forget to log off when you're done";;
*)        #*通配符即匹配所有情况
echo "Sorry, you are not allowed here";;
esac      #case语句结束

13. 更多的结构化命令

13.1 for命令

#!/bin/bash
# another example of how not to use the for command
for test in I don\'t know if "this'll" work #不添加反斜杠会被视作一个字符串
do                                          #如果一个词语中有空格,需要用双引号圈起来
echo "word:$test"
done

13.1.1 list可以从命令中读取

#!/bin/bash
# reading values from a file
file="states"
for state in $(cat $file)
do
echo "Visit beautiful $state"
done

13.1.2 更改字段分隔符

for语句中的list中,环境变量 IFS(内部字段分隔符)默认会用空格、制表符、换行符作为分隔符
+ IFS.OLD=IFSIFS=IFSIFS=’\n’:;” :使用换行、冒号、分号作为分隔符
<在代码中使用新的IFS值>
IFS=$IFS.OLD :修改使只识别换行符

13.2 C 语言风格的 for 命令

#!/bin/bash
# testing the C-style for loop
for (( i=1; i <= 10; i++ ))  #1.变量赋值可以有空格
do                           #2.条件中的变量不以美元符开头
echo "The next number is $i" #3.迭代过程的算式未用 expr 命令格式
done
##############################
# multiple variables
for (( a=1, b=10; a <= 10; a++, b-- ))
do
echo "$a - $b"
done
##############################

13.3 while命令

#!/bin/bash
# testing a multicommand while loop
var1=10
while echo $var1  #检查 var1 是否大于等于 0
[ $var1 -ge 0 ]   #只有最后一个测试命令的退出状态码会被用来决定什么时候结束循环
do                #每次迭代中所有的测试命令都会被执行,区别于C语言的 || 
echo "This is inside the loop"
var1=$[ $var1 - 1 ]
done

13.4 until 命令

#!/bin/bash
# using the until command
var1=100
until echo $var1  #shell会执行指定的多个测试命令,只有在最后一个命令成立时停止
[ $var1 -eq 0 ]   #退出状态码不为0,才执行循环中列出的命令
do                # -eq :检查是否相等
    echo Inside the loop: $var1
    var1=$[ $var1 - 25 ]
done

13.5 嵌套循环

$ cat test14
#!/bin/bash
# nesting for loops
for (( a = 1; a <= 3; a++ ))
do
    echo "Starting loop $a:"
    for (( b = 1; b <= 3; b++ ))
    do
        echo " Inside loop: $b"
    done
done

13.6 循环处理文件数据

#!/bin/bash
# changing the IFS value
# 通过改变IFS的值处理 /etc/passwd文件
IFS.OLD=$IFS
IFS=$'\n'      #先按行处理文件数据
for entry in $(cat /etc/passwd)
do
    echo "Values in $entry –"
    IFS=:      #再把每一行用空格分开
    for value in $entry
    do
        echo " $value"
    done
done

13.7 控制循环

  • break 命令
#!/bin/bash
# breaking out of an outer loop
for (( a = 1; a < 4; a++ ))
do
    echo "Outer loop: $a"
    for (( b = 1; b < 100; b++ ))
    do
        if [ $b -gt 4 ]
        then
            break 2      #1.注意,加上参数这里可以跳出两层循环
        fi
        echo " Inner loop: $b"
    done
done
  • continue 命令
#!/bin/bash
# continuing an outer loop
for (( a = 1; a <= 5; a++ ))
do
    echo "Iteration $a:"
    for (( b = 1; b < 3; b++ ))
    do
        if [ $a -gt 2 ] && [ $a -lt 4 ]
        then
            continue 2   #1.加上参数指定要继续执行哪一级循环
        fi
        var3=$[ $a * $b ]
        echo " The result of $a * $b is $var3"
    done
done

13.8 处理循环的输出

#!/bin/bash
for file in /home/hzq/*
do
    if [ -d "$file" ]
    then
        echo "$file is a directory"
    elif [ -f "$file" ]
    then
        echo "$file is a file"
    fi
done

13.9 实例

  • 13.9.1 查找可执行文件(通过PATH)
#!/bin/bash
# finding files in the PATH

IFS=:
for folder in $PATH  #1.遍历每一个PATH目录中的路径
do
    echo "$folder:"  #2. 打印这些路径
    echo "$IFS"      
    for file in $folder/* #为什么IFS是":",但是这里还是能分开
    do
        echo "$IFS" 
        if [ -x $file ]
        then
            echo " $file"
        fi
    done
done

14. 处理用户输入

14.1 命令行参数的读取

#!/bin/bash
# using one command line parameter
#
factorial=1
for (( number = 1; number <= $1 ; number++ ))
do
    factorial=$[ $factorial * $number ]
done
echo The factorial of $1 is $factorial
if [ -n "$1" ]&&[ -n "$2" ]            #0.注意:这里必须加引号
then
    total=$[ $1 * $2 ]                 #1.一个参数乘以第二个参数
    echo The first parameter is $1.    #2.参数也可以是字符串,以空格分隔
    echo The second parameter is $2.   #3.如果参数不止9个,可以用花括号:${10}
    echo The total value is $total.
else
    echo "please input two number"
fi
echo "$0 is run in the end"             #4.$0保存的是脚本的名字
#潜在问题:如果使用另一个命令来运行shell脚本,命令会和脚本名混在一起,出现在 $0 参数中。
echo "$(basename $0)is run in the end " #不过可以这样解决

14.2 特殊参数变量

#!/bin/bash
# testing $* and $@
#
echo
count=1
#
for param in "$*"   #$* :所有的参数
do
    echo "\$* Parameter #$count = $param"
    count=$[ $count + 1 ]
done
#
echo
count=1
#
for param in "$@"
do               #$@ :将参数视作单独的单词
    echo "\$@ Parameter #$count = $param"
    count=$[ $count + 1 ]
done

14.3 移动参数

#!/bin/bash
# demonstrating the shift command
echo
count=1
while [ -n "$1" ]
do
echo "Parameter #$count = $1"
count=$[ $count + 1 ]
shift  2                   #向左移动参数,但是$0不变,加上参数表示一次移动两个
done

14.4 处理选项

  • 使用getopt命令
    set -- $(getopt -q ab:cd "$@")
#!/bin/bash
# Extract command line options & values with getopt
#
set -- $(getopt -q ab:cd "$@")
#
echo
while [ -n "$1" ]
do
    case "$1" in
        -a) echo "Found the -a option" ;;
        -b) param="$2"
            echo "Found the -b option, with parameter value $param"
            shift ;;
        -c) echo "Found the -c option" ;;
        --) shift
            break ;;
        *) echo "$1 is not an option";;
    esac
    shift
done
#
count=1
for param in "$@"
do
    echo "Parameter #$count: $param"
    count=$[ $count + 1 ]
done
#需要注意的是:getopt 命令并不擅长处理带空格和引号的参数值,如下面这种情况   
#$ ./32_getopt.sh -a -b test1 -cd "test2 test3" test4
  • 更加高级的getopts
#!/bin/bash
# Processing options & parameters with getopts
#
echo
while getopts :ab:cd opt              #指明要查找哪些命令行选项,以及每次迭代中存储它们的变量名(opt)
do
case "$opt" in
    a) echo "Found the -a option" ;;  #注意这里没有单破折号,已经移除了
    b) echo "Found the -b option, with value $OPTARG" ;;
    c) echo "Found the -c option" ;;
    d) echo "Found the -d option" ;;
    *) echo "Unknown option: $opt" ;;
esac
done
#
shift $[ $OPTIND - 1 ]
#
echo
count=1
for param in "$@"
do
    echo "Parameter $count: $param"
    count=$[ $count + 1 ]
done
#./32_getopt.sh -b "test1 test2" -a   :现在可以正常解析空格了
#./32_getopt.sh -abtest1              :将选项字母和参数值放在一起使用,而不用加空格
#./32_getopt.sh -acde                 :将命令行上找到的所有未定义的选项统一输出成问号

14.5 将选项标准化

14.6 获得用户的输入

#!/bin/bash
# testing the read -p option
#
#
echo -n "Enter your name: "             # -n 参数使不换行
read name
echo "Hello $name, welcome to my program. "
                                        # -t 指定等待的秒数    
read  -t 5 -p "Enter your name: "       # 不指定变量,数据会放入特殊环境变量REPLY中
echo Hello $REPLY, welcome to my program.

read -p "Please enter your age: " age   # -p 命令直接指定提示字符串
days=$[ $age * 365 ]
echo "That makes you over $days days old! "

read -n1 -p "Do you want to continue [Y/N]? " answer
case $answer in                         # -n 参数指定接收的输入个数
Y | y) echo
echo "fine, continue on…";;
N | n) echo
echo OK, goodbye
exit;;
esac
                                        # -s 参数避免在命令中输入的数据出现在显示器上
read -s -p "Enter your password: " pass #事实是会显示,只是跟背景色一样
echo "Is your password really $pass? "

14.6.1 从文件中读取

#!/bin/bash
# reading data from a file

count=1
test="34_read_input.sh"
cat $test | while read line    #1.每次读取一行
do                             #2.
    echo "Line $count: $line"
    count=$[ $count + 1]
done
echo "Finished processing the file"

15. 呈现数据

15.1 理解输入和输出

  • ls -l > test2 :重定向输出
  • cat < test2 :重定向输入
  • who >> test2 :将数据追加到文件
  • ls -al badfile > test3 :注意这句,错误不会输出到显示器,但是会创建test3文件
  • ls -al badfile 2> test4 :只重定向错误消息, 因为系统设置STDERR 描述符是2
  • ls -al test test2 test3 badtest 2> test6 1> test7 :重定向错输出和错误
  • ls -al test test2 test3 badtest &> test7 :将 STDERR 和 STDOUT 的输出重定向到同一个输出文件,注意 shell自动赋予了错误消息更高的优先级

15.2 在脚本中重定向输出

  • 临时重定向
    echo "This is an error" >&2 :有意生成一条错误信息
  • 永久重定向
    exec 1>testout :用 exec 命令告诉shell在脚本执行期间重定向某个特定文件描述符
  • 在脚本中重定向输入
    exec 0< testfile : exec 命令允许你将 STDIN 重定向到Linux系统上的文件中

14.3 创建自己的重定向

  • 创建输出文件描述符
#!/bin/bash
# using an alternative file descriptor
exec 3>test13out                 #可以用 exec 命令来给输出分配文件描述符
#exec 3>>test13out               #也可以使用 exec 命令来将输出追加到现有文件中
echo "This should display on the monitor"
echo "and this should be stored in the file" >&3
echo "Then this should be back on the monitor"
  • 重定向文件描述符
#!/bin/bash
# storing STDOUT, then coming back to it
exec 3>&1                                   #1.文件描述符3重定向到标准输出
exec 1>test14out                            #2.标准输出重定向到文件
echo "This should store in the output file"
echo "along with this line."
exec 1>&3                                   #3.利用该文件描述符3重定向回 STDOUT
echo "Now things should be back to normal"  #+.这句输出到屏幕
  • 创建输入文件描述符
#!/bin/bash
# redirecting input file descriptors
exec 6<&0          #1.先用文件描述符 6 用来保存 STDIN 的位置
exec 0< testfile   #2.将 STDIN 重定向到一个文件
count=1
while read line    #3.read命令的所有输入都来自重定向后的 STDIN
do
echo "Line #$count: $line"
count=$[ $count + 1 ]
done
exec 0<&6          #4.将 STDIN 恢复到原先的位置
read -p "Are you done now? " answer
case $answer in
Y|y) echo "Goodbye";;
N|n) echo "Sorry, this is the end.";;
esac
  • 创建读写文件描述符
#!/bin/bash
# testing input/output file descriptor
exec 3<> testfile       #1.用exec命令将文件描述符 3 分配给文件 testfile 以进行文件读写
read line <&3           #2.用 read 命令读取文件中的第一行
echo "Read: $line"      #3.需要注意:写入文件中的数据会覆盖已有的数据
echo "This is a test line" >&3
  • 关闭文件描述符
#!/bin/bash
# testing closing file descriptors
exec 3> test17file
echo "This is a test line of data" >&3
exec 3>&-                   #1.要关闭文件描述符,将它重定向到特殊符号 &-
echo "This won't work" >&3  #2.这里shell会生成错误消息
cat test17file              #3.打开了同一个输出文件,shell会用一个新文件来替换已有文件
exec 3> test17file         
echo "This'll be bad" >&3   #4.所以意味着这几句话会覆盖已有文件

15.5 列出打开的文件描述符

  • /usr/sbin/lsof -a -p $$ -d 0,1,2
    • -p选项指定进程
    • -d指定要显示的文件描述符
    • 特殊环境变量$$表示当前进程的PID
    • -a选项用来对其他选项的结果进行布尔AND运算
      ### 15.6 阻止命令输出
    • ls -al > /dev/null :shell输出到null文件的任何数据都不会保存,全部都被丢弃
    • cat /dev/null > testfile :这个文件通常用它来快速清除现有文件中的数据
      ### 15.7 创建临时文件
    • mktemp testing.XXXXXX
    • mktemp 命令可以在/tmp目录中创建一个唯一的临时文件
    • 不会用默认的 umask 值
    • 参数可以是文件模板后面跟6个X
 #!/bin/bash
#creating and using a temp file
tempfile=$(mktemp test19.XXXXXX)      #生产一个临时文件
exec 3>$tempfile                      #输出重定向到该文件
echo "This script writes to temp file $tempfile"
echo "This is the first line" >&3     
echo "This is the second line." >&3
echo "This is the last line." >&3 
exec 3>&-                             #关闭文件描述符
echo "Done creating temp file. The contents are:"
cat $tempfile
rm -i $tempfile 2> /dev/null          #删除文件,并把删除的提示信息输出到/dev/null,即不显示
  • mktemp -t test.XXXXXX :强制在/tmp目录下创建文件,返回全路径
#!/bin/bash
# using a temporary directory
tempdir=$(mktemp -d dir.XXXXXX)   #创建临时文件夹
cd $tempdir
tempfile1=$(mktemp temp.XXXXXX)   
tempfile2=$(mktemp temp.XXXXXX)
exec 7> $tempfile1                #重定向文件描述符到文件
exec 8> $tempfile2
echo "Sending data to directory $tempdir"
echo "This is a test line of data for $tempfile1" >&7
echo "This is a test line of data for $tempfile2" >&8

15.8 记录消息

  • date | tee -a testfile
    • date输出的消息同时输出的显示器和文件testfile
    • -a选项表示是以追加的方式写入

16. 控制脚本

16.1 处理信号

#!/bin/bash
# Modifying a set trap
#
trap "echo ' Sorry... Ctrl-C is trapped.'" SIGINT
              #捕获"终止进程"信号
count=1
while [ $count -le 5 ]
do
    echo "Loop #$count"
    sleep 1
    count=$[ $count + 1 ]
done
#
trap "echo ' I modified the trap!'" SIGINT
               #到这里处理信号的方式已经变了
count=1
while [ $count -le 5 ]
do
    echo "Second Loop #$count"
    sleep 1
    count=$[ $count + 1 ]
done
trap -- SIGINT #删除设置好的捕获,单破折号也能起作用
echo "Ctrl-C can use le"
count=1
while [ $count -le 5 ]
    echo "Second Loop #$count"
    sleep 1
    count=$[ $count + 1 ]
done

16.2 以后台模式运行脚本

  • ./test4.sh & :只需要像这样,在命令后面加一个&,但是这样还是会有输出,不要奇怪

16.3 在非控制台下运行脚本

  • nohup ./test1.sh & :可以用nohup命令,阻断所有发送给该进程的SIGHUP信号,输出会被保存到nohup.out文件中

16.4 作业控制

  • jobs -l
    • jobs 命令可以查看分配给shell的作业
    • -l 参数显示作业的PID
    • -n 只列出上次shell发出的通知后改变了状态的作业
    • -p 只列出作业的PID
    • -r 只列出运行中的作业
    • -s 只列出已停止的作业
    • 带加号的作业会被当做默认作业
    • 当前的默认作业完成处理后,带减号的作业成为下一个默认作业
  • bg :以后台模式重启一个作业,如果有多个就得加作业号
  • fg 2:以前台模式重启作业,可用带有作业号的 fg 命令

16.5 调整谦让度

从-20(最高优先级)到+19(最低优先级),默认以0优先级启动,可以记做”好人难做”
+ nice -n 10 ./test4.sh > test4.out &:nice命令指定运行的优先级
+ ps -p 4973 -o pid,ppid,ni,cmd,可以用这个命令验证
+ -n 选线不是必须的,只需要指定优先级就行了
+ 但是像降低优先级必须得有root权限
+ renice -n 10 -p 5055
+ renice命令允许改变已经运行进程的优先级
+ 只能对属于你的进程执行 renice
+ 只能通过 renice 降低进程的优先级
+ root用户可以通过 renice 来任意调整进程的优先级

16.6 定时运行作业

  • at [-f filename] time:指定Linux系统何时运行脚本
    • 如:at -M -f ./test.sh 10:15
      • 10:15 PM :AM/PM指示符格式
      • now、noon、midnight或者teatime(4 PM) :特定可命名时间
      • MMDDYY、MM/DD/YY或DD.MM.YY:标准日期格式
      • Jul 4或Dec 25:文本日期,加不加年份均可
      • 也可以指定时间增量,(当前时间+25 min,明天10:15 PM,10:15+7天)
    • 可以用 -q 参数指定不同的队列字母
    • -f 用来指定脚本文件
    • -M 选项来屏蔽作业产生的输出信息
  • atq:查看系统中有哪些作业在等待
  • atrm 18:根据作业号删除指定作业

16.7 cron

  • cron时间表
    • min hour dayofmonth month dayofweek command
    • 15 10 * * * command :每天15.10运行一个命令
    • 15 16 * * 1 command:每周一4:15 PM运行的命令
    • 00 12 1 * * command :每个月的第一天中午12点执行命令
    • 00 12 * * * if [ date +%d -d tomorrow = 01 ] ; then ; command :每个月的最后一天执行的命令
    • 15 10 * * * /home/rich/test4.sh > test4out :可以添加任何想要的命令行参数和重定向符号
  • 构建cron时间表
    • crontab -l :列出corn时间表
    • 可以用 -e 选项来添加条目
  • 浏览cron目录(有4个基本目录:hourly、daily、monthly和weekly)
    • ls /etc/cron.*ly :查看这四个目录
    • 如果脚本需要每天运行一次,只要将脚本复制到daily目录,cron就会每天执行它
  • anacron程序
    • anacron程序只会处理位于cron目录的程序,比如/etc/cron.monthly
    • 每个cron目录都有个时间戳文件,该文件位于/var/spool/anacron
    • anacron程序使用自己的时间表(通常位于/etc/anacrontab)来检查作业目录
    • anacron时间表的基本格式和cron时间表略有不同:
      • period delay identifier command

16.8 使用新的shell启动脚本

  • 依照下列顺序所找到的第一个文件会被运行,其余的文件会被忽略:
    • $HOME/.bash_profile
    • $HOME/.bash_login
    • $HOME/.profile

17. 创建函数

17.7 在命令行上使用函数

  • function divem { echo $[ $1 / $2 ]; }:采用单行方式定义函数
    • divem 100 5 :调用单行定义的函数
  • function doubleit { read -p "Enter value: " value; echo $[$value * 2 ]; }:需要注意的是得加分号,这样才知道起止位置

    function multem {  
      echo
    function multem {    echo
    [ 11∗2 ]
    }

    • 也可以像这样采用多行定义的方式
    • . /home/rich/libraries/myfuncs:可以在bashrc文件末尾加上你自己的函数库

19. 初识sed和gawk

19.1 文本处理

  • sed options script file
    • -e script :在处理输入时,将 script 中指定的命令添加到已有的命令中
    • -f file :在处理输入时,将 file 中指定的命令添加到已有的命令中
    • -n :不产生命令输出,使用 print 命令来完成输出
    • 例如:
      • echo "This is a test" | sed 's/test/big test/'
      • sed 's/dog/cat/' data1.txt :不过不会修改文本文件的数据,只是显示到STDOUT
      • sed -e 's/brown/green/; s/dog/cat/' data1.txt :使用多个命令需要用-e参数
      • sed -e ' :然后把命令一条一条输进去
      • sed -f script1.sed data1.txt :也可以把上述三条命令放进一个文件,通过-f参数指定这个文件,去处理data1.txt
  • gawk options program file
    • -F fs :指定行中划分数据字段的字段分隔符
    • -f file :从指定的文件中读取程序
    • -v var=value :定义gawk程序中的一个变量及其默认值
    • -mf N :指定要处理的数据文件中的最大字段数
    • -mr N :指定数据文件中的最大数据行数
    • -W keyword :指定gawk的兼容模式或警告等级
    • 例如:
      • gawk '{print "Hello World!"}' :对每行文本执行脚本
      • gawk '{print $1}' data2.txt:打印date2.txt文件中每行的第一个单词,默认是空白字符或者制表符作为分隔符
      • gawk -F: '{print $1}' /etc/passwd:-F指定字段分隔符
      • echo "My name is Rich" | gawk '{$4="Christine"; print $0}':要执行多个命令,只需要用”;”分隔
      • gawk '{:也可以用此提示符一行一行的输入
      • gawk -F: -f script2.gawk /etc/passwd:可以把命令放到script.gawk文件中,通过-f参数运行
  • sed '2,$s/test/trial/' data4.txt 3gpw data4s.txt :转义字符前加”\”
    • 3 :表明替换第几处
    • g :替换所有匹配的文本
    • p :原先的内容要打印出来
    • w file :将替换的结果写入文件中
    • 2,$ :第二行到后面的所有行
  • sed '/Samantha/s/bash/csh/' /etc/passwd:Samantha这里可以替换成正则表达式,所以整句只查找正则表达式匹配到的行
  • 0_1526906298982_8c89fd74-c2e1-4707-917a-d9677c30d758-image.png
    也可以像这样组合表达式
  • sed '3d' data6.txt:删除第三行
  • sed '2,3d' data6.txt:删除2~3行
  • sed '3,$d' data6.txt:删除第三行开始以后的所有行
  • sed '/number 1/d' data6.txt:删除出现number 1的行
  • sed '/1/,/3/d' data6.txt:第一个模式打开删除功能,第二个模式关闭删除功能,sed编辑器会删除两个指定行之间的所有内容,如果后面又匹配到1,又会打开删除模式
  • echo "Test Line 2" | sed 'i\Test Line 1':-i参数在指定行前面插入一行
  • echo "Test Line 2" | sed 'a\Test Line 1':-a参数在指定行后面添加一行
  • sed '3i\> This is an inserted line.' data6.txt:这是一个将新行插入到第三行前面的例子
  • sed '$a\> This is a new line of text.' data6.txt附加到末尾
  • sed '3c\ This is a changed line of text.' data6.txt:-c参数指定修改模式,这里修改第三行
  • sed '/number 3/c\ This is a changed line of text.' data6.txt:也可以通过模式匹配的方式修改
  • sed '2,3c\ This is a new line of text.' data6.txt:也可以使用区间地址,但是这一行会覆盖2、3两行
  • sed 'y/123/789/' data8.txt:1替换成9,2替换成8,3替换成9,直到替换完所有的字符
  • sed -n '/number 3/p' data6.txt:-p参数表打印模式,-n参数用来禁止输出其他行
  • 输出之前打印
    0_1526908393422_77918f24-e501-4eb7-a376-4144570c9b8e-image.png
  • sed '=' data1.txt:”=”打印的会输出行号
  • sed -n 'l' data9.txt:打印数据流中的文本和不可打印的ASCII字符
  • sed '1,2w test.txt' data6.txt:将数据流中的前两行打印到文件中
  • sed '3r data12.txt' data6.txt:将data12.txt中的数据插入到data6.txt的第三行后面
  • sed '/number 2/r data12.txt' data6.txt:将data12中的数据插入到匹配行的后面
  • sed '$r data12.txt' data6.txt:插入到末尾
  • 0_1526909880112_0d833613-ca9e-4146-9486-6462d2889efa-image.png
    • 找到notice.std中包含LIST的那一行,并用data11.txt中的文本代替

20. 正则表达式

20.1 定义BRE模式

  • echo "This is a test 1" | sed -n '/test 1/p':p命令输出匹配到的行,注意空格和其他字符并没有什么区别
  • echo "This is a test" | gawk '/tes/{print $0}':print命令输出匹配到的行,注意两种写法都区分大小写,不用写出完整单词
  • 特殊字符:.*[]^${}\+?|()
    • sed -n '/\$/p' data2:使用特殊字符必须转义
    • echo "3 / 2" | sed -n '/\//p':注意,使用正斜线也必须转义
  • 锚字符
    • echo "Books are great" | sed -n '/^ Book/p':Book必须出现在行首才能匹配到
    • echo "This ^ is a test" | sed -n '/s ^ /p':脱字符放在其他地方就变成普通字符了
    • echo "This is a good book" | sed -n '/book$/p':字符”$”指明数据行必须以该文本结尾
    • sed -n '/^ this is a test$/p' data4:会忽略那些不单单只包含这些文本的行
    • sed '/^ $/d' data5:删除文本中的空白行
    • sed -n '/.at/p' data6:”.”匹配任意一个字符,如果没有(如行首)则匹配失败
    • sed -n '/[ch]at/p' data6:[ch]字符组取代”.”使通配符精确一点,不过必须有一个字符组中的字符被匹配到
    • sed -n ' /^ [0123456789][0123456789]$/p ' data8:匹配只出现两个数字的一行
  • sed -n '/[^ ch]at/p' data6:在字符组的开头加上脱字符,就成了了排除型字符组
  • sed -n '/^[0-9][c-h][a-ch-m]$/p' data8:单破折号表示linux字符集中得字符,即这样可以表示区间,最后一个区间表示a~c,h~m
  • 特殊字符

    这里写图片描述

  • echo "ieeeek" | sed -n '/ie*k/p':字符后面放*,表示该字符要出现一次或多次

  • echo "this is a regular pattern expression" | sed -n '/regular.*expression/p':.*的组合表示若干字符

20.2 扩展正则表达式

  • echo "bt" | gawk '/b[ae]?t/{print $0}':?表示字符组出现了一次或零次,另外,注意sed不支持扩展的正则表达式
  • echo "beeet" | gawk '/be+t/{print $0}':加号表示+前面的字符至少出现一次
  • echo "bet" | gawk --re-interval '/be{1}t/{print $0}':{}中的1表示e刚好出现了一次
  • echo "bt" | gawk --re-interval '/be{1,2}t/{print $0}':{}中的1,2表示至少出现一次,最多出现两次
  • echo "The cat is asleep" | gawk '/cat|dog/{print $0}':管道允许匹配两个模式中的任何一个
  • echo "Saturday" | gawk '/Sat(urday)?/{print $0}':()能对字符进行分组,即表示urday是一个整体

20.3 正则表达式实战

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!