我们一起来学Shell - 初识shell


我们一起来学Shell - 初识shell
我们一起来学Shell - shell的变量
我们一起来学Shell - shell的条件判断
我们一起来学Shell - shell的循环控制
我们一起来学Shell - shell的数组
我们一起来学Shell - shell的函数
我们一起来学Shell - shell的并发及并发控制
我们一起来学Shell - 正则表达式

Shell 的分类

  • Linux中默认的Shell是/bin/bash,流行的Shell有ashbashkshcshzsh等,不同的Shell都有自己的特点以及用途。

    bash

    大多数Linux系统默认使用的Shell,bash ShellBourne Shell 的一个免费版本,它是最早的Unix Shell

    bash还有一个特点,可以通过help命令来查看帮助

    包含的功能几乎可以涵盖Shell所具有的功能,所以一般的Shell脚本都会指定它为执行路径。

    csh

    csh是具有C语言风格的一种Shell,其内部命令有52个,较为庞大。

    目前使用的并不多,已经被/bin/tcsh所取代。

    ksh

    Korn Shell 的语法与 Bourne Shell 相同,同时具备了 C Shell 的易用特点。

    许多安装脚本都使用ksh

    ksh 有42条内部命令,与bash相比有一定的限制性。

    tcsh

    tcshcsh的增强版,与 csh 完全兼容。

    sh

    是一个快捷方式,已经被 /bin/bash 所取代。

    nologin

    指用户不能登录

    zsh

    目前Linux里最庞大的一种 zsh。

    它有84个内部命令,使用起来也比较复杂。

    一般情况下,不会使用该Shell。

Shell 能做什么

  • 自动化批量系统初始化程序 (update,软件安装,时区设置,安全策略…)
  • 自动化批量软件部署程序 (LAMP,LNMP,Tomcat,LVS,Nginx)
  • 应用管理程序 (KVM,集群管理扩容,MySQL,DELLR720批量RAID)
  • 日志分析处理程序(PV, UV, 200, !200, top 100, grep/awk)
  • 自动化备份恢复程序(MySQL完全备份/增量 + Crond)
  • 自动化管理程序(批量远程修改密码,软件升级,配置更新)
  • 自动化信息采集及监控程序(收集系统/应用状态信息,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL)
  • 配合Zabbix信息采集(收集系统/应用状态信息,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL)
  • 自动化扩容(增加云主机——>业务上线)
  • Zabbix监控CPU 80%+|-50% Python API AWS/EC2(增加/删除云主机) + Shell Script(业务上线)
  • 俄罗斯方块,打印三角形,打印圣诞树,打印五角星,运行小火车,坦克大战,排序算法实现
  • Shell可以做任何事(一切取决于业务需求)

bash 环境变量文件

/etc/profile

全局(公有)配置,不管是哪个用户,登录时都会读取该文件。

/etc/bashrc

Ubuntu 没有此文件,与之对应的是 /ect/bash.bashrc

它也是全局(公有)的

bash 执行时,不管是何种方式,都会读取此文件。

~/.profile

若 bash 是以 login 方式执行时,读取 ~/.bash_profile,

若它不存在,则读取 ~/.bash_login,

若前两者不存在,读取~/.profile。

图形模式登录时,此文件将被读取,即使存在 ~/.bash_profile 和 ~/.bash_login。

~/.bash_login

若 bash 是以 login 方式执行时,读取 ~/.bash_profile,

若它不存在,则读取 ~/.bash_login,

若前两者不存在,读取 ~/.profile。

~/.bash_profile

Unbutu 默认没有此文件,可新建。

只有 bash 是以 login 形式执行时,才会读取此文件。

通常该配置文件还会配置成去读取 ~/.bashrc。

~/.bashrc

当 bash 是以 non-login 形式执行时,读取此文件。

若是以 login 形式执行,则不会读取此文件。

.bashrc 文件内写一行

source /etc/profile

每执行一次shell,都会执行一次bashrc,然后就会执行source,避免环境变量报错

~/.bash_logout

注销时,且是 longin 形式,此文件才会读取。

在文本模式注销时,此文件会被读取,

图形模式注销时,此文件不会被读取。

bash 环境变量加载

  • 图形模式登录时,顺序读取:/etc/profile~/.profile
  • 图形模式登录后,打开终端时,顺序读取:/etc/bash.bashrc~/.bashrc
  • 文本模式登录时,顺序读取:/etc/bash.bashrc/etc/profile~/.bash_profile
  • 从其它用户 su 到该用户,则分两种情况:
    • 如果带 -l 参数(或-参数,--login 参数),如:su -l username,则 bash 是 login 的,它将顺序读取以下配置文件:/etc/bash.bashrc/etc/profile~/.bash_profile
    • 如果没有带 -l 参数,则 bash 是 non-login 的,它将顺序读取:/etc/bash.bashrc~/.bashrc
  • 注销时,或退出 su 登录的用户,如果是 longin 方式,那么 bash 会读取:~/.bash_logout
  • 执行自定义的 Shell 文件时,若使用 bash -l a.sh 的方式,则 bash 会读取行:/etc/profile~/.bash_profile
    • 若使用其它方式,如:bash a.sh./a.shsh a.sh(这个不属于bash Shell),则不会读取上面的任何文件
  • 上面的例子,凡是读取到 ~/.bash_profile 的,若该文件不存在,则读取 ~/.bash_login,若前两者不存在读取 ~/.profile

login shell与non-login shell

login shell

取得 bash 时需要完整的登陆流程的,就称为 login shell

举例来说,我们登陆终端时,需要输入用户的账号与密码,此时取得的 bash 就称为 login shell

non-login shell

取得 bash 时不需要完整的登陆流程的 ,就称为 non-login shell

在原本的 bash 环境下再次使用 bash 这个命令,建立了一个bash子进程,没有输入账号密码, 那第二个 bash (子程序) 也是 non-login shell

bash 特性

命令和文件自动补齐

很多命令都会提供一个 bash-complete 的脚本,在执行该命令时,敲 tab 可以自动补全参数,会极大提高生产效率。

linux命令自动补全需要安装 bash-completion (yum install -y bash-completion)

注意: 断开终端,重新连接后可正常 tab 补齐

默认情况下,Bash 为 Linux 用户提供了下列标准补全功能。

  • 变量补全
  • 用户名补全
  • 主机名补全
  • 路径补全
  • 文件名补全

命令历史记忆功能

Bash 有自动记录命令的功能,自动记录到.bash_history隐藏文件中。还可以在下次需要是直接调用历史记录中的命令

centos 可以通过/etc/profile中的文件来定义一些参数、

在bash中,使用history 命令来查看和操作之前的命令,以此来提高工作效率。

history是bash的内部命令,所以可以使用 help history 命令调出 history 命令的帮助文档(history默认保存1000行)。

关于history的一些用法,可以看我的另一篇博客:有个姑娘叫history

别名功能

alias 命令

别名的好处是可以把本来很长的指令简化缩写,来提高工作效率。

alias # 查看系统当前所有的别名
alias h5='head -5' # 定义新的别名。这时候输入h5就等于输入’head-5‘
unalias h5 # 取消别名定义

如果想要文件永久生效,只需将上述别名命令写到对应用户或者系统 bashrc 文件中

如果想用真实命令可以在命令前面添加反斜杠 ,使别名失效

关于alias的一些用法,可以看我另一篇博客:linux可以这样玩 之 杂乱无章的随笔(不定期更新){搜索:利用alias传参,自定义linux命令}

快捷键

快捷键不区分大小写,大写是为了方便区分

快捷键作用
ctrl+A把光标移动到命令行开头。如果我们输入的命令过长,想要把光标移动到命令行开头时使用。
ctrl+E把光标移动到命令行结尾。
ctrl+C强制终止当前的命令。
ctrl+L清屏,相当于clear命令。
ctrl+U删除或剪切光标之前的命令。我输入了一行很长的命令,不用使用退格键一个一个字符的删除,使用这个快捷键会更加方便
ctrl+K删除或剪切光标之后的内容。
ctrl+W删除或剪切光标之前的内容(注意:和浏览器的快捷键会有冲突,浏览器的快快捷键是关闭当前标签页)。
ctrl+Y粘贴ctrl+U或ctul+K剪切的内容。
ctrl+R在历史命令中搜索,按下ctrl+R之后,就会出现搜索界面,只要输入搜索内容,就会从历史命令中搜索。
ctrl+D退出当前终端。
ctrl+Z暂停,并放入后台。这个快捷键牵扯工作管理的内容,我们在系统管理章节详细介绍。
ctrl+S暂停屏幕输出。
ctrl+Q恢复屏幕输出。

前后台作业控制

Linux bash Shell单一终端界面下,经常需要管理或同时完成多个作业,如一边执行编译,一边实现数据备份,以及执行SQL查询等其他的任务。所有的上述的这些工作可以在一个 bash 内实现,在同一个终端窗口完成。

前后台作业的定义
  • 前后台作业实际上对应的也就是前后台进程,因此也就有对应的 pid。在这里统称为作业。

  • 无论是前台作业还是后台作业,两者都来自当前的Shell,是当前Shell的子程序。

  • 前台作业:可以由用户参与交互及控制的作业我们称之为前台作业。

  • 后台作业:在内存可以自运行的作业,用户无法参与交互以及使用[ctrl]+c来终止,只能通过bg或fg来调用该作业。

几个常用的作业命令
  • command & 直接让作业进入后台运行

  • [ctrl]+z 将当前作业切换到后台

  • jobs 查看后台作业状态

  • fg %n 让后台运行的作业n切换到前台来

  • bg %n 让指定的作业n在后台运行

  • kill %n 移除指定的作业n

    • "n" 为jobs命令查看到的job编号,不是进程id。
    • 每一个job会有一个对应的job编号,编号在当前的终端从1开始分配。
    • job 编号的使用样式为[n],后面可能会跟有 “+” 号或者 “-” 号,或者什么也不跟。
      • “+” 号表示最近的一个job,
      • “-” 号表示倒数第二个被执行的Job。
      • 注,“+” 号与 “-” 号会随着作业的完成或添加而动态发生变化。
  • 通过jobs方式来管理作业,当前终端的作业在其他终端不可见。

作业脱机管理
  • 将作业(进程)切换到后台可以避免由于误操作如[ctrl]+c等导致的job被异常中断的情形,而脱机管理主要是针对终端异常断开的情形。
  • 通常使用nohup命令来使得脱机或注销之后,Job依旧可以继续运行。也就是说nohup忽略所有挂断(SIGHUP)信号。
  • 如果该方式命令之后未指定&符号,则job位于前台,指定&符号,则job位于后台。
  • 不加&

不加&,作业会在一个新的终端运行,当前终端是无法通过jobs命令查看到信息,只能通过ps -ef命令查看进程

由于使用的是sleep命令,并且没有加&,所以ps -ef | grep sleep会出现两个进程,父进程pid不会变,子进程pid会一直在变,因为他会不断的打开新的终端执行sleep这个命令,所以,如果要停止这个作业,需要使用kill 父进程pid命令

nohup sleep
  • 加&

使用jobs命令,可以查看到当前作业的运行情况(sleep命令后面的数字单位是秒,如果数字写的小,很快就退出了,jobs会查看不到作业)

不指定日志目录的情况下,会将日志写入到当前目录下的nohup.out文件中

nohup sleep 3000 &

指定日志目录的方法

注释:>& 等同于 2>&1&>,将正确的和错误的日志,都写入到指定的文件中

nohup sleep 3000 >& ./sleep.log &

命令执行的顺序

&& 具备逻辑判断

command1 && command2 只有在 command1 成功执行后才会执行 command2

|| 具备逻辑判断

command1 || command2 在 command1 没有成功执行时执行command2

; (分号)不具备逻辑判断

command1 ; command2 command1无论是否成功执行,执行完成后,都会去执行commadn2

通配符(元字符)表示的不是本意

常见的通配符

注意与正则稍有不同

字符含义实例
*匹配0个或多个任意字符a*b,a与b之间可以有任意长度的字符,也可以没有。例如:aabcb,ab,azxcb…
?匹配一个任意字符a?b,a与b之间必须但也只能存在一个字符,该字符可以是任意字符。例如:aab,abb,acb…
[list]匹配list中的任意单个字符a[xyz]b,a与b之间必须但也只能存在一个字符,该字符只能是x或y或z。例如:axb,ayb,azb
[!list]匹配除list中的任意单个字符a[!a-z]b,a与b之间必须但也只能存在一个字符,该字符不能是小写字母。例如:aAb,a0b…
[c1-c2]匹配c1-c2间的任意单个字符a[0-1]b,a与b之间必须但也只能存在一个字符,该字符只能是数字。例如:a0b,a1b…
{string1,string2,…}匹配string1、string2等中的一个字符串a{abc,xyz,opq}b,a与b之间必须但也只能存在一个字符串,字符串只能是abc或xyz或opq。例如:aabcb,axyzb,aopqb…

Shell 脚本规范

Shell脚本绝大部分命令自己平时也经常使用,但是在写成脚本的时候总觉得写的很难看。而且当我在看其他人写的脚本的时候,总觉得难以阅读。毕竟Shell脚本这个东西不算是正经的编程语言,他更像是一个工具,用来杂糅不同的程序供我们调用。因此很多人在写的时候也是想到哪里写到哪里,基本上都像是一段超长的main函数,不忍直视。同时,由于历史原因,Shell有很多不同的版本,而且也有很多有相同功能的命令需要我们进行取舍,以至于代码的规范很难统一。
 考虑到上面的这些原因,我查阅了一些相关的文档,发现这些问题其实很多人都考虑过,而且也形成了一些不错的文章,但是还是有点零散。因此我就在这里把这些文章稍微整理了一下,作为以后我自己写脚本的技术规范。

风格规范

开头有“蛇棒”
所谓shebang其实就是在很多脚本的第一行出现的以”#!”开头的注释,他指明了当我们没有指定解释器的时候默认的解释器,一般可能是下面这样:

#!/bin/bash

上面这种写法可能不太具备适应性,一般我们会用下面的方式来指定:

#!/usr/bin/env bash

除了 bash 之外,可以用下面的命令查看本机支持的解释器:

cat /etc/shells

注释

  • 注释的意义不仅在于解释用途,而在于告诉我们注意事项,就像是一个 README。
  • 具体的来说,对于Shell脚本,注释一般包括下面几个部分:
    • shebang
    • 脚本的参数
    • 脚本的用途
    • 脚本的注意事项
    • 脚本的写作时间,作者,版权等
    • 各个函数前的说明注释
    • 一些较复杂的单行命令注释

参数要规范

  • 这一点很重要,当脚本需要接受参数的时候,一定要先判断参数是否合乎规范,并给出合适的回显,方便使用者了解参数的使用。

变量

  • 一般情况下会将一些重要的环境变量定义在开头,确保这些变量的存在。
  • 一段好的代码通常是不会有很多硬编码在代码里的“魔数”的。如果一定要有,通常是用一个变量的形式定义在开头,然后调用的时候直接调用这个变量,这样方便日后的修改。

缩进

  • 因为很多需要缩进的地方(比如if,for语句)都不长,很多人都懒得去缩进,而且很多人不习惯用函数,导致缩进功能被弱化。

  • 正确的缩进是很重要的,尤其是在写函数的时候,否则在阅读的时候很容易把函数体跟直接执行的命令搞混。

  • 常见的缩进方法主要有"soft tab"和"hard tab"两种:

    • 所谓soft tab就是使用n个空格进行缩进(n通常是2或4)
    • 所谓hard tab当然就是指真实的"\t"字符
  • 对于if和for语句之类的,最好不要把then,do这些关键字单独写一行,这样看上去比较丑。

命名有标准

所谓命名规范,基本包含下面这几点:

  • 文件名规范,以.sh结尾,方便识别
  • 变量名字要有含义,不要拼错
  • 统一命名风格,写 Shell 一般用小写字母加下划线

编码要统一

在写脚本的时候尽量使用 UTF-8 编码,能够支持中文等一些奇奇怪怪的字符。不过虽然能写中文,但是在写注释以及打log的时候还是尽量英文,毕竟很多机器还是没有直接支持中文的,打出来可能会有乱码。

日志和回显

  • 日志的重要性不必多说,能够方便回头纠错,在大型的项目里是非常重要的。
  • 如果这个脚本是供用户直接在命令行使用的,那么最好还要能够在执行时实时回显执行过程,方便用户掌控。
  • 为了提高用户体验,会在回显中添加一些特效,比如颜色啊,闪烁啊之类的。

密码要移除

  • 不要把密码硬编写在脚本里。

太长要分行

  • 在调用某些程序的时候,参数可能会很长,这时候为了保证较好的阅读体验,我们可以用反斜杠来分行:
./configure \
–prefix=/usr \
–sbin-path=/usr/sbin/nginx \
–conf-path=/etc/nginx/nginx.conf

注意:在反斜杠前有个空格。

代码有效率

  • 在使用命令的时候要了解命令的具体做法,尤其当数据处理量大的时候,要时刻考虑该命令是否会影响效率。
  • 比如下面的两个sed命令:
sed -n '1p' file
sed -n '1p;1q' file 12

作用一样,都是获取文件的第一行。

但是第一条命令会读取整个文件,而第二条命令只读取第一行。

当文件很大的时候,仅仅是这样一条命令不一样就会造成巨大的效率差异。

当然,这里只是为了举一个例子,这个例子真正正确的用法应该是使用head -n1 file命令

勤用双引号

  • 几乎所有的大佬都推荐在使用"$"来获取变量的时候最好加上双引号。
  • 不加上双引号在很多情况下都会造成很大的麻烦,
#!/bin/sh
# 已知当前文件夹有一个a.sh的文件
var="*.sh"
echo $var
echo "$var" 

运行结果如下:

a.sh
*.sh

可以解释为它执行了下面的命令

echo *.sh
echo "*.sh"

在很多情况下,在将变量作为参数的时候,一定要注意上面这一点,仔细体会其中的差异。上面只是一个非常小的例子,实际应用的时候由于这个细节导致的问题实在是太多了

学会查路径

  • 很多情况下,会先获取当前脚本的路径,然后以这个路径为基准,去找其他的路径。通常我们是直接用 pwd 以期获得脚本的路径。
  • 不过其实这样是不严谨的,pwd 获得的是当前Shell的执行路径,而不是当前脚本的执行路径。
  • 正确的做法应该是下面这两种
script_dir=$(cd $(dirname $0) && pwd)
script_dir=$(dirname $(readlink -f $0 ))

应当先cd进当前脚本的目录然后再pwd,或者直接读取当前脚本的所在路径。

代码要简短

  • 这里的简短不单单是指代码长度,而是只用到的命令数。原则上我们应当做到,能一条命令解决的问题绝不用两条命令解决。这不仅牵涉到代码的可读性,而且也关乎代码的执行效率。
  • 最最经典的例子如下
cat /etc/passwd | grep root
grep root /etc/passwd

cat 命令最为人不齿的用法就是这样,用的没有任何意义,明明一条命令可以解决,非得加根管道

使用新写法

这里的新写法不是指有多厉害,而是指可能更希望使用较新引入的一些语法,更多是偏向代码风格

  • 尽量使用func( ){ }来定义函数,而不是func{ }
  • 尽量使用[[ ]]来代替[ ]
  • 尽量使用$()将命令的结果赋给变量,而不是反引号
  • 在复杂的场景下尽量使用printf代替echo进行回显

其他小技巧

  • 路径尽量保持绝对路径,不容易出错,如果非要用相对路径,最好用./修饰
  • 优先使用bash的变量替换代替awk sed,这样更加简短
  • 简单的if尽量使用&&||,写成单行。比如[[ x > 2]] && echo x
  • 当export变量时,尽量加上子脚本的namespace,保证变量不冲突
  • 会使用trap捕获信号,并在接受到终止信号时执行一些收尾工作
  • 使用mktemp生成临时文件或文件夹
  • 利用/dev/null过滤不友好的输出信息
  • 会利用命令的返回值判断命令的执行情况
  • 使用文件前要判断文件是否存在,否则做好异常处理
  • 不要处理ls后的数据(比如ls -l | awk '{ print $8 }'),
    • ls的结果非常不确定,并且平台有关
  • 读取文件时不要使用for loop而要使用while read

Shell 脚本调试

Shell脚本的语法调试,使用bash的相关参数进行调试

sh [参数] 文件名.sh
  • -n 不要执行script,仅查询语法的问题
  • -v 在执行script之前,先将script的内容输出到屏幕上
  • -x 将使用的脚本的内容输出到屏幕,该参数经常被使用

脚本运行方式

Linux中Shell脚本的执行通常有4种方式,分别为工作目录执行,绝对路径执行,sh执行,Shell环境执行。

工作目录执行

工作目录执行,指的是执行脚本时,先进入到脚本所在的目录(此时,称为工作目录),然后使用 ./脚本方式执行,这种方式,需要脚本有执行权限,否则会报错Permission denied

绝对路径执行

绝对路径中执行,指的是直接从根目录/到脚本目录的绝对路径,同样也是需要脚本有执行权限

sh 执行

sh执行,指的是用脚本对应的sh或bash来接着脚本执行,所以,脚本不是必须要有执行权限

Shell 环境执行

Shell环境执行,指的是在当前的Shell环境中执行,可以使用 .接脚本 或 source 接脚本

. test.sh

注意,点后面有空格

source test.sh
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值