Skip to main content

3. Shell

David LiuAbout 11 min

3. Shell

基本语法

  1. 注释:#
  2. 输出:echo
  3. 输入:read, 从标准输入读入一个字符串
  4. 换行: \, 一条单一长命令内也可以换行

第一行文件头#!,标志该文件是 shell 脚本类型。

#!/bin/sh

文件头:文件的前 16 个 bit(前两个字节/英文字符)是 magic number,来说明文件类型,然后用后面的东西解释(sh 可以 ls 也可以,都行,但是能做的内容不一样多)

eg. 0xCAFEBABE 是 Java class 类型

连接命令

在 Linux 中,可以使用以下符号来连接两个命令:

  1. 分号 ;: 分号用于分隔两个命令,不考虑前一个命令的执行结果,无论成功与否都会继续执行后续命令。

  2. 逻辑与 &&: 逻辑与运算符用于连接两个命令,只有前一个命令成功执行(返回状态码为 0)后,才会执行后续命令。

  3. 逻辑或 ||: 逻辑或运算符用于连接两个命令,只有前一个命令失败执行(返回状态码非 0)后,才会执行后续命令。

这些符号可以根据需要在 shell 脚本或命令行中使用,以控制命令的执行顺序和条件。

command1; command2 && command3 || command4
command1 \
  && command2 \
  && command3 \
  || command4

执行方法

  • sh [文件名字]
  • chmod -x [文件名字] 增加执行权限,然后 [文件名字]
  • source [文件名字],将会在当前 shell 中运行 execute 命令
  • . [文件名字],将会在当前 shell 中运行 execute 命令,可以接入/修改环境中定义的变量

脚本语言里面,变量基本上不需要声明类型,直接=赋值即可

shell 变量名称喜欢全大写,

  • 单引号:单独当作字符串本身,不会进行转义
  • 双引号:会进行转义变量或表达式
  • 连续的字符可以省略引号,但是如果有括号、空格等符号,则“单引号、双引号、花括号等”无法省略。

变量类型

在 Shell 脚本中,变量的类型是动态的,也就是说,Shell 不需要显式声明变量的类型。以下是在 Shell 中常见的变量类型:

  1. 字符串变量: 用于存储文本数据。

    name="John"
    
  2. 整数变量: 用于存储整数数据。

    age=25
    
  3. 数组变量: 用于存储多个值的列表。

    fruits=("apple" "banana" "orange")
    
  4. 关联数组变量: 用于存储键值对的列表。

    declare -A person
    person["name"]="John"
    person["age"]=25
    
  5. 环境变量: 用于存储系统环境和配置信息。

    PATH="/usr/local/bin:/usr/bin:/bin"
    
  6. 只读变量:不允许修改的变量readonly a="sss"

  7. 局部变量:local,声明函数等代码块中的局部变量

请注意,Shell 中的变量默认都是字符串类型,包括整数变量。如果需要进行数值计算,Shell 会自动进行类型转换。如果需要显式地将变量解释为整数,可以使用declaretypeset命令。

declare -i num=10

此外,还可以使用readonly关键字将变量声明为只读,以防止其被修改。

readonly name="John"

这些是 Shell 脚本中常见的变量类型。Shell 还支持其他特殊类型,如文件描述符和命令替换等。确保在编写 Shell 脚本时参考相关文档和语法规范。

可选变量

getopts命令获取和解析命令行选项(options)。解析单个字符的命令行。每个选项使用一个字母表示,后面可以跟一个冒号表示该选项需要一个参数值。

#!/bin/bash

while getopts ":a:b:c" opt; do
  case $opt in
    a)
      echo "Option -a is set with value $OPTARG"
      ;;
    b)
      echo "Option -b is set with value $OPTARG"
      ;;
    c)
      echo "Option -c is set"
      ;;
    \?)
      echo "Invalid option: -$OPTARG"
      ;;
  esac
done

变量引用

就是在变量名前面 + $符号

$HELLO
${HELLO}
${HELLO:3:4} #引用切片完整版,取3开始的4个字符
${HELLO::4} #开头的4个字符,可以随意缺省部分
${HELLO:3} #3开头往后所有字符
${apps[0]} #apps数组的第0位
${object["name"]} #object关联数组的第name字段

变量引用切片,第一位是起始索引,第二位是保留的位数

字符串操作

$HELLO
${HELLO}
${HELLO:3:4} #引用切片完整版,取3开始的4个字符
${HELLO::4} #开头的4个字符,可以随意缺省部分
${HELLO:3} #3开头往后所有字符
${string^} #首字母大写
${string^^} #全部大写
${string,} #首字母小写
${string,,} #全部小写
${string:start:length}
${#string} #字符串长度
${string/pattern/replacement} #替换第一个匹配项
${string//pattern/replacement} #替换所有匹配项

特殊变量

$# #传入的参数个数
$0 #sh脚本的名
$n #就是输入的第 n 个参数
$@ #所有参数作为独立字符串返回
$* #将所有命令行参数作为单个字符串返回,参数之间使用第一个字符在IFS环境变量中指定的分隔符进行分隔(默认为空格)。
$? #The status of last CMD,上一个命令的返回值(成功是0,失败是1等)
$$ #PID
$! #最后一个在后台运行的进程的PID
${PIPESTATUS[@]} #上一行指令的所有管道的状态
${BASH_SOURCE[0]} #Bash内置的数组,包含了当前正在执行的脚本文件的路径。

字符串(变量值)拼接,直接 echo 后面变量挨着写即可

变量默认值(常用于取不一定设置的环境变量时)

${COLOR:-green} #无的时候用-后面替换
${COLOR:=green} #无的时候用=后面替换,并将这个变量赋值
${#COLOR} #获取COLOR变量的字符串长度
${#COLORS[@]} #获取COLORS数组的长度
${!prefix*} #获取以prefix开头的变量名列表
${!prefix@} #获取以prefix开头的变量名列表

ps 看进程

env 是 set 的子集

export 可以把 set 里面的变量升级为环境变量

环境变量:可以通过父进程传递给子进程的

环境变量是在操作系统中定义的全局变量,可以在整个系统中被访问和使用。它们存储了与系统操作和配置相关的信息,如路径、默认设置和用户首选项等。

在 Shell 脚本中,可以通过以下方式来访问和使用环境变量:

  1. 查看环境变量:使用env命令可以列出当前系统中定义的所有环境变量。

    env
    

    或者使用printenv命令:

    printenv
    
  2. 获取环境变量的值:可以使用$符号后跟环境变量的名称来获取其值。

    echo $HOME
    

    上述示例中,$HOME表示HOME环境变量的值。

  3. 设置环境变量:可以使用export命令来设置环境变量。

    export MY_VARIABLE="Hello World"
    

    上述示例中,MY_VARIABLE是新定义的环境变量,其值为Hello World。通过export命令,将其设置为全局环境变量,可以在脚本的其他地方访问该变量。

  4. 取消环境变量:可以使用unset命令来取消设置的环境变量。

    unset MY_VARIABLE
    

    上述示例中,MY_VARIABLE环境变量将被取消,并从环境中移除。

环境变量在 Shell 脚本中非常有用,可以用于存储和传递系统配置、用户首选项和其他重要信息。通过合理地使用环境变量,可以提高脚本的灵活性和可配置性。

PATH=.:$PATH #就是加上这个"."#当前目录
恢复的话就是,PATH=${PATH:2}

条件测试

  • test 条件
  • [ 条件 ]

测试工具主要就是$?

其实就是在运行一些命令(实际上并不是在变成)

0-255,只用了 8 个 bit

shell 里面 0 是真,其他都是假(与 C 相反)

权限测试

  • test -w 文件名,测试他的执行权限
  • [ -w a.c ]方括号语法,注意空格一个都不能差,不然报错

字符测试

  • !=:
  • =:
  • =~: 正则表达式匹配

数字测试

  • -gt
  • -lt
  • -ne
  • -eq

命令替换

`ls` # 执行命令
$(ls) # 执行命令
<(ls) # 用作将命令的输出作为文本进行处理

loop

for

字符串有空格分割的话,for each 的时候就相当于数组

for variable in list
do
    # 执行操作
done

命令行参数扩展:{prefix..suffix},如果有多组的话会排列组合,数字可以多位数字,但是字符只能单位字符,否则不解析,字符类会在 ascII 表上的排序。

for i in {1..5}; do
    # 执行操作,例如打印数字
    echo "$i"
done
for ((i=start; i<=end; i++)); do
    # 执行操作,例如打印数字
    echo "$i"
done

while & until

while condition
do
    # 执行操作
done
counter=1
until [ $counter -gt 5 ]
do
    echo $counter
    counter=$((counter + 1))
done

执行数学运算$((1+1))

branch

if

if [condition]
then
   # code
elif [condition]
then
   # code
else [condition]
   # code
fi

case

case $opt in
   pattern1)
      ;;
   pattern2)
      ;;
   *)
      ;;
   \?)
      ;;
esac

I/O

echo

read,读取用户输入的字符

tee 可以把命令的流分成两叉,常用在既把输出到文件又输出他

重定向

输出重定向

>: 标准输出重定向,覆盖原有内容

>>: 标准输出追加重定向,将命令的标准输出追加到文件中,如果文件不存在则创建。写在末尾

2>: 标准错误输出重定向

n>: 文件描述符输出重定向

$>, >$: 标准输出和错误输出合并重定向

m>&n: 文件描述符合并重定向

输入重定向

<: 标准输入重定向

<<: Here Document,通过文档重定向将一块文本作为命令的输入。在 Here Document 中,文本块的起始标记和结束标记必须单独占据一行,并且结束标记必须顶格写。标记的命名可以根据实际情况来选择,但必须保证起始标记和结束标记一致。

n<: 文件描述符输入重定向

引号

' '只是当做字符串

" "可以转义里面的变量或者公式进行输出

` `命令替换

函数

想要获取输入的参数就用,$1, $2 等

return 返回值,只能是在 0-255 之间的整数,调用者通过$?获取

字符可以用echo,调用者通过$(func)调用并获取结果

include:.

随机数

$RANDOM: 生成一个介于 0 到 32767 之间的随机整数

cat /dev/urandom | od -N2 -An -i | awk -v f=1 -v r=100 '{printf "%i\n", f + r * $1 / 65536}'
random_number=$(od -An -N2 -i /dev/urandom | awk -v min=1 -v max=10000 '{print min + int(($1 \* (max-min+1)) / 32768)}')

echo "随机数: $random_number"

这条命令的作用是生成一个在给定范围内随机分布的整数。

具体原理如下:

cat /dev/urandom: /dev/urandom 是 Linux 系统中的一个特殊设备文件,可以读取其内容来获取伪随机数。cat 命令用于将 /dev/urandom 的内容输出到标准输出。

od -N2 -An -i: od 命令用于将二进制数据转换为不同的格式进行展示。-N2 表示只读取两个字节,-An 表示不输出地址,-i 表示将字节解析为整数。

awk -v f=1 -v r=100 '{printf "%i\n", f + r * $1 / 65536}': 这部分利用 awk 命令对输入进行处理。首先,-v f=1 和 -v r=100 分别定义了 awk 的变量 f 和 r,分别表示范围的起始值和区间长度。然后,{printf "%i\n", f + r * $1 / 65536} 用于计算并输出在给定范围内的随机整数。其中,$1 表示 awk 的第一个输入字段,即前一步 od 命令输出的整数,65536 是一个常数,用于将取值范围归一化到 0~1 之间,然后利用乘法和加法操作将其映射到给定的范围内。

综合起来,这条命令的原理是从 /dev/urandom 中读取随机字节,然后通过 od 命令转换为整数,并通过 awk 命令将这些整数映射到给定的范围内,生成随机分布的整数。

set

set 命令可以用来查看当前的 shell 参数设置。它可以显示当前 shell 的各种设置:

  • 显示已定义的变量和函数。
  • 显示 shell 的设置选项,如:
    • -e(如果有语句执行错误,就立即退出)
    • -u(如果使用未初始化的变量,则报错)
  • 显示当前的位置参数。
  • 如果在 set 命令后面跟上选项,可以用来修改 shell 的设置。

具体选项如下:

  • set: 显示所有已经定义的变量和函数
  • set -o: 显示当前的 shell 设置(选项)
  • set -e(或-o errexit):如果有任何命令执行失败(返回非零状态码),则立即退出 shell。
  • set -u(或-o nounset):如果使用未初始化的变量,则报错并退出 shell。
  • set -x(或-o xtrace):调试模式,在执行每个命令之前,显示命令的详细输出。
  • set -o pipefail: 如果管道中的任何命令失败,则整个管道被视为失败。
  • set -n: 只进行语法检查,而不执行命令。
  • set -f: 禁用文件名扩展(通配符)。
  • set -v(或 -o verbose):在执行命令之前显示命令的详细输出。
  • set +H(或 +o histexpand):禁用命令行历史展开。
  • set +B(或 +o braceexpand):禁用大括号扩展。
  • set +C(或 +o noclobber):禁止使用重定向创建已存在的文件。