Shell编程学习记录

本文主要是记录学习 Shell 编程时所记录的一些笔记。

Shell 脚本的格式

在 Linux 中有很多命令,每一条命令都可以完成某种特定的任务,这也是 UNIX 的设计哲学:一条命令只做一件事。因此为了组合命令以及便于多次执行,可以使用脚本文件来保存需要执行的命令。

创建一个以 .sh 为后缀的文件,我们可以在其中编写很多指令。如果执行的是刚编写好的脚本文件,那么在执行前还需要为其赋予可执行权限 chmod u+rx filename

说到 Shell 脚本执行方式,有下面几种:

  1. bash ./filename.sh
  2. ./filename.sh
  3. source ./filename.sh
  4. .filename.sh

在 1、2 中,会创建一个名叫 bash 的子进程,Shell 脚本就会运行在该子进程中,两者的区别就在于 2 需要在脚本开头使用 #!/bin/bash 来指定使用 bash,而方式 1 在执行时就明确需要使用 bash 了。方式 3、4 则是在当前进程中执行 Shell 脚本。

如何理解前两种和后两种执行方式的区别呢?比如在 Shell 脚本中存在切换目录的操作 cd ,在前两种方式中,在脚本执行完成后,当前的路径是不会被改变的,因为是使用另一个进程执行的该脚本,而后两种方式在脚本执行完成后,当前路径就会被改变,因为它就是在当前进程下执行的。

有时候我们会发现 Shell 脚本的第一行往往都是 #!/bin/bash,写这一行代码有什么作用呢?或者说有什么好处呢?

简而言之,在第一行开头使用 #! 可以在文件中指定脚本的解析方式,在使用 bash filename.sh 来执行 Shell 时,就是直接指定 bash 作为解析方式,文件内以 # 开头的部分都会被当作注释。而在使用 ./filename.sh 来执行脚本文件时,会使用系统自带的 Shell 来解析脚本文件,遇到以 #! 开头的行时就会被当成非注释,因此在开头写上 #!/bin/bash 就表示需要使用 bash 来解析脚本文件。换句话说,如果第一行写的是,#!/usr/bin/python 或者 #!/usr/bin/python3 就表示该文件需要使用 Python 来解析,即使文件后缀为 .sh

管道与重定向

  • 管道:进程通信的主要工具,在 Shell 中是为了方便两条命令能够进行通信
  • 重定向:将标准输出输出到指定的位置

管道

因为在 Shell 编程中,管道是没有名字的,所以也叫匿名管道或者管道符。管道符是 | ,其作用就是将前一个命令执行的结果传递给后面的命令。比如,echo 123 | cat

值得注意的是,管道会为两条命令创建一个子进程来进行通信,所以在使用 cdpwd 等内部命令时,当前的进程是得不到相应的结果的。

重定向

一个进程默认会打开标准输入、标准输出、错误输出三个文件描述符

  • 输入重定向 < 比如 read var < /path/filename
  • 输出重定向 >>>2>&> 比如 echo 123 > /path/filename
    • > 清空文件内容并输入
    • >> 在文件末尾追加
    • 2> 当命令执行错误时,会将错误信息输出到指定文件
    • &> 无论命令执行成功与否,都会将信息输出到指定文件中

变量

定义 变量的命名与大多数编程语言的区别相差不大,都是由字母、数字以及下划线组成且不以数字开头。

变量的赋值

  • 变量名=变量值= 两边不允许出现空格,因为出现空格后,变量名会被当作命令,因此会报错)
    • a=123
  • 使用 let 为变量赋值
    • let a=10+20
  • 将命令赋值给变量
    • l=ls
  • 将命令结果赋值给变量,使用 $() 或者 ''
    • letc=$(ls -l /etc)
  • 变量值有空格等特殊字符可以包含在 ""''

变量的引用

定义完变量后就需要使用它,使用方式为 ${变量名},当然如果变量在引用时不会引发歧义的话,是可以省略掉 {} 的,什么叫不会引发歧义呢?

1
2
3
4
5
6
7
8
s1='hello'

# 直接引用变量,往往是可以省略掉 {}
echo $s1

# 如果还需要在变量之后衔接其他东西,那么就能会识别成别的变量,导致无法输出想要的值
echo $s123 # 没有该变量,输出空串
echo ${s1}23 # 输出 hello23

此外,还有一种使用场景,如果我需要使用的变量为空值,是否可以使用别的值来代替空值,而当变量被被复赋值后,又可以直接输出变量值而非控制?有一种才做叫做变量替换,形式为 变量=${引用变量-替换值}

1
2
3
4
5
# 设定 path 为文件名,如果为空则输出下划线 _
path=${filename-_}

echo path
# 如果设定了 filename 的值,则输出该值,否则会输出下划线 _

变量的作用范围

变量的默认作用范围是只在当前的进程当中,也就是说,别的进程想要引用另外一个进程的变量是取不到的。

这就需要用到之前提及过的 Shell 脚本的几种解析方式了,如果我们想在脚本中使用该进程中设置的其他变量,就需要使用到 source filename.sh 或者 . filename.sh,因为这样脚本就会运行在当前进程下。

  • 那如果子进程也想访问父进程中的变量,该如何实现呢?

    可以使用 export 关键字,通过 export 变量 的方式,让别的进程也可以放到这一变量。

  • 如果想要删除设置过的变量,又该如何实现呢?

    可以使用 unset 关键字,通过 unset 变量 来删除变量。

系统环境的变量

系统的环境变量都为大写字母加下划线的组合,要查看有哪些环境变量可以使用命令 env | more 或者 set | more 来查看,使用 set 可以查看更多的命令,这里还是用了管道加 more 指令的方式来分页查看命令

常见的系统变量,比如有 PATH,当我们要使用非系统自带的指令时,就需要将可执行命令的路劲追加PATH 中,也就是使用冒号 : 来分割 PATH=$PATH:新的命令路径,并记得用 export 来到处 PATH,这样在使用时,系统就可以找到需要使用的命令。

除了系统变量外,还有一些预定义变量

  • $?:用来表示上一条命令是否成功执行,成功为 0, 失败为 1
  • $$:用来查看当前进程的 PID,在对脚本的运行状态以及监测时会用到
  • $0:用来查看当前进程的名称

此外,还有用于为脚本传递参数的位置变量,比如 $1$2$3$4$5$6$7$8$9${10}…… ……

它们分别用来代替第一个脚本参数以及之后的参数,这样我们在编写 Shell 脚本时就可以很方便的指代传进来的参数。

环境变量的配置

环境变量的配置文件有很多个

  • /etc/profile 保存系统或终端启动时的运行环境,在登录情况下,会被第一个加载
  • ~/.bash_profile
  • /etc/profile.d/ 这是一个目录,根据不同的 Shell 版本会执行下面不同的脚本
  • ~/.bashrc
  • /etc/bashrc

简单区别,在 etc 目录下的配置文件,是所有用户通用的;而其他跟在 ~ 后的配置文件,表示在家(home)目录下,当前用户特有的配置文件。此外,除了目录的不同,还区分了 profilebashrc 这几种配置文件,这是因为登录当前用户的情况分为 loggin shellno login shell,也就是说对于 login shell 会使用当前的所有配置文件,而对于 no login shell 则会只使用 bashrc 的文件。

各个配置文件在加载时也分先后,相同的变量,后加载的会覆盖掉先加载的。在登陆状态下,配置文件的加载顺序为

  1. /etc/profile
  2. ~/.bash_profile
  3. ~/.bashrc
  4. /etc/bashrc

而在非登录的情况下,配置文件是加载不完全的,其加载顺序如下:

  1. ~/.bashrc
  2. /etc/bashrc

值得注意的是,这些配置文件都是在每次打开终端或者 Shell 时会去加载,因此如果新添加了环境变量是不会立即生效的,所以要使得环境变量立即生效可以使用 source 来重新加载添加了新环境变量的的配置文件。

数组

  • 定义数组 array=( 1 2 3 4 5 ) 元素间用空格区分
  • 显示数组的所有元素 echo ${array[@]}
  • 显示数组元素个数 ```echo $
Author: Inno Fang
Link: http://innofang.github.io/2020/04/05/Shell%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-ND 4.0 unless stating additionally.