Shell编程基础入门
都依凡
撰写于 2023年 04月 18 日

Shell脚本和编程

1. 学习Shell的价值

  1. Linux服务器的基本操作和管理
  2. 前端node.js 服务的进程管理、问题排查、资源监控等运维操作
  3. 使用 shell 编写 TCE、SCM、Docker 脚本,完成服务编译和部署

    Linux很多指令都需要 Shell 完成

2. 学习环境依赖

  1. 一台安装了 Linux 系统的物理机或者云主机,可运行 Shell 脚本
  2. 本地的VScode 安装 Bash DeBug 插件,并且升级 bash 到 4.X 以上
  3. NPM全局安装 zx 依赖

3. Shell构成

shell官方对Shell的定义是命令行解释器,也是一门编程语言。

  • Shell 作为解释器

    • 可以解释脚本和命令
    • 同时bash 内置了命令
    • GNU 提供了很多工具,如: is 、mv、cat等
    • 第三方库:社区或自己编写的一些库,如:node、Python等封装的模块
  • Shell 作为编程语言

    • 变量:可以自定义变量、使用变量的能力
    • 运算:可以进行逻辑运算和算数运算
    • 语句:判断、分支、循环等编程语言具备的特点
    • 函数:定义函数和使用函数的能力

4. Hello Shell

  1. 在Linux中创建文件test.sh
  2. 在文件中编辑如下内容
#! /bin/bash  
echo "Hello Shell!"
  • 代码中第一行:#! 是一个约定的标记 表示告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 程序,人们并不区分 Bourne Shell 和 Bourne Again Shell,所以,像 #!/bin/sh,它同样也可以改为 #!/bin/bash
  • 第二行:echo 表示输出显示打印 引号内表示要输出的语句

5. 执行 Shell 脚本

要想执行Shell脚本,首先要看看对应的shell脚本文件是否拥有执行权限,可以使用 ll 命令查看,若没有执行权限,可以使用

chmod +x <shell脚本文件名>
  1. 执行脚本命令
# 中间不可以有空格
./<文件名.sh>
  1. 执行脚本命令二
sh <shell脚本文件名>

6. Shell的语法

1. Shell变量

69

2. declare [ + / - ] <选项> <变量>的参数

55

3. Shell自定义变量

# 变量名=变量值(等号左右不可以有空格,否则shell会当成一个命令)如:
name="张三"
age=21
score=100

# 将命令复制给变量
_ls=ls

# 将命令结果赋值给变量
files=$(ls -a)

# Shell默认的数据类型是字符串,如下面代码不会进行数学运算
num=age*score

# 数学运算需要声明变量为整型
let nums=age*score

declare -i nums=age*score

4. Shell变量的使用

在shell中使用 $ 符号对已经声明的变量进行使用

#! /bin/bash
size=2
page_num=3

let nums=size+page_num
echo "size="$size
echo "page_num="$page_num
echo "相加得"$nums
# 输出结果 
# size=2
# page_num=3
# 相加得5

5. 系统环境变量

69

系统变量可以直接在linux中进行使用直接使用如:echo $HOME

6. Shell 运算符

58

7. Shell 传递参数

在执行Shell脚本时,可以给Shell脚本传递参数,脚本内获取参数格式为$n 其中 n 代表参数传递过来的是第几个参数,如有下面shell脚本

#! /bin/bash

echo "第一个参数$0"
echo "第二个参数$1"
echo "第三个参数$2"
echo "第四个参数$3"

加入Shell脚本文件名为test.sh脚本执行的时候可以传参,传参方式如下:

sh test.sh 1 2 3 4
# 执行结果
第一个参数test2.sh
第二个参数1
第三个参数2
第四个参数3

总结:

我们会发现其中 $0 获取的是执行的shell文件名称,而自动忽略了第四个参数不显示,所以在Shell脚本中,若参数多的时候会忽略

8. Shell特殊字符处理参数

参数处理说明
$#传递到脚本的参数个数
$*以一个单字符串显示所有向脚本传递的参数。 如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。
$$脚本运行的当前进程ID号
$!后台运行的最后一个进程的ID号
$@与$*相同,但是使用时加引号,并在引号中返回每个参数。 如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
$-显示Shell使用的当前选项,与set命令功能相同。
$?显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

1.Shell脚本特殊字符处理参数($*)和($@)的区别

如:test.sh 文件中有如下代码

#!/bin/bash
echo "-- \$* 演示 ---"
for i in "$*"; do
    echo $i
done

echo "-- \$@ 演示 ---"
for i in "$@"; do
    echo $i
done

则输出结果

-- $* 演示 ---
1 2 3
-- $@ 演示 ---
1
2
3

我们可以从输出中看到$*会将传送的变量当作一个整体,而$@会将传输的参数散开,当成三个参数使用

7. Shell输入输出重定向

Shell 分为标准输入(fd0),标准输出(fd1),标准错误输出(fd2)

  • 输出重定向符号
符号含义
>覆盖式写入文件
>>追加写入文件
2>错误输出写入文件
&>正确和错误输出统一写入文件中
  • 输出重定向符号
符号含义
<
<<
需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。

8. Shell判断命令

  1. Shell 中提供了test[[[ 三种判断符号,可以使用到
  • 整数判断
  • 字符串测试
  • 文件测试
  1. 语法:
  • test <参数>
  • [ <参数> ]
  • [[ <参数> ]]
注意:
    中括号前后要有空格符
    [ 和 `test` 命令只能使用自己支持的标志位,<、>、= 只能用来比较字符串,不能比较整数
    中括号内的变量,最好都使用引号括起来
    [[ 更丰富,在整形比较中,支持<、>、= 在字符串中支持 =~ 正则
  1. 测试代码
#! /bin/bash
# 整数测试
test $n1 -eq $n2 # 两个整数变量等于
test $n1 -lt $n2 # 两个整数变量小于
test $n1 -gt $n2 # 两个整数变量大于

# 字符串测试
test -z $str # 判断字符串为空 -z
test -n $str # 判断字符串非空 -n
test $ str1 = $str2 # 判断字符串相等 =

# 文件测试
test -e /dmt && echo "exist" # 文件是否存在 -e
test -f /usr/bin/npm && echo "file exist" # 文件是否存在,并且是一个普通文件

9. Shell 条件判断

if <条件>
then
    代码块
elif <条件>
then
    代码块
else
    代码块
fi

判断两个变量是否相等

a=10
b=20
if [ $a == $b ]
then
   echo "a 等于 b"
elif [ $a -gt $b ]
then
   echo "a 大于 b"
elif [ $a -lt $b ]
then
   echo "a 小于 b"
else
   echo "没有符合的条件"
fi

如果使用 ((.....))

a=10
b=20
if (( $a == $b ))
then
   echo "a 等于 b"
elif (( $a > $b ))
then
   echo "a 大于 b"
elif (( $a < $b ))
then
   echo "a 小于 b"
else
   echo "没有符合的条件"
fi

10.Shell函数

  • 语法一
funName(){
    方法体
}
  • 语法二
function funName(){
    方法体
}

使用方法:

function add(){
    let sum=$1+$2
    echo sum
}
# 调用方式
* add 1 2
# 是不需要写括号的

在Shell中是不需要声明参数的,在方法内使用$进行参数获取

注意:

  • Shell 是自上而下执行的,函数定义在前,使用在后
  • 函数获取变量和 Shell script类似,$0 代表函数名称
  • 函数内return 仅仅表示函数的执行状态,不代表函数的执行结果
  • 返回结果一般使用echoprintf,在外边使用$()、``获取结果
  • 如果没有 return 函数的状态是上一条命令的执行状态,存储在$?

11. 模块化

模块化的原理是在当前的Shell内执行函数文件,引入方式:suorce [函数库的路径]

如,现在有文件test1.sh

#! /bin/bash
function add(){
    let sum=$1+$2
    echo $res
}

有第二个文件main.sh

#! /bin/bash
source './math.sh'
sum=$(add 1 2)
echo $sum

12. Shell脚本的执行过程

  1. 字符解析

    • 识别换行符,分号,做行的分割
    • 识别命令连接符(|| 、&&、 管道)做命令的分割
    • 识别空格,tab符号,做命令和参数的分割
  2. Shell 展开,如:{1..3} 解析为 1 2 3 后面会详细说
  3. 重定向,将标准文件的输入输出进行指向的变更
  4. 执行命令

    • 内置命令会在当前进程直接执行
    • 非内置命令会使用$PATH 查找,然后启动子进程执行
  5. 手机状态并返回

13. Shell 展开

1. 大括号展开

一般由三部分构成,前缀、一对大括号,后缀,大括号内可以是逗号分割的字符串序列,也可以是序列表达式

# 字符序列
a{b,c,d}e 展开结果=> abe ace ade
# 表达式序列 (数字可以使用..调整增量,字母不能调整增量)
{1..5}  =>  1 2 3 4 5

{1..5..2}  => 1 3 5

{a..e}   =>  a b c d e

2. 波浪号展开

波浪号会将当前替换成当前用户主目录的值

~  展开是 # $HOME就是用户目录 

~root  =>  # root指定用户的目录

~+    => # 当前工作目录

~-    =>  # 上一个工作目录

3. 大括号展开

  1. 间接参数扩展${!parameter},其中应用的并不是parameter 而是 parameter的实际值
parameter="var"
var="hello"
echo ${!parameter}
# 首先会将parameter转换成var
# 其次${var}的值是hello
# 所以最后输出的值是 hello
  1. 参数长度 ${parameter}

输出parameter的长度

par=cd
echo ${#par} 
# 输出 2
  1. 空参数处理

    1. ${parameter:-word} 为空替换 判断parameter的值是否为空,为空使用word替换
    2. ${parameter:=word} 为空替换,判断parameter的值是否为空,为空将parameter的值赋值为word
    3. ${parameter:?word} 为空报错
    4. ${parameter:+word} 不为空替换

如,有下面Shell脚本

a=1
echo ${a:-wrod} # 1

echo ${b:-wrod} # word

echo ${par:=wrod} # word

echo ${par:-hello} # word 因上一条语句已经赋值为word,所以不为空,所以输出结果还是word

echo ${a:+foo} # foo
  1. 参数切片

${parameter:offset}

${parameter:offset:lenth}

  1. 参数部分删除,对字符掐头去尾的操作

    1. ${parameter%word} # 最小限度从后面截取word
    2. ${parameter%%word} # 最大限度从后面截取word
    3. ${parameter#word} # 最小限度从前面截取word
    4. ${parameter##word} # 最大限度从前面截取word
#! /bin/sh
str=abcdefg

sp1=${str##*d}

sp2=${str%%d*}

echo $sp1 # 输出efg

echo $sp2 # 输出abc

14. Shell脚本VScode好用插件推荐

  1. Shellman :代码提示和自动补全
  2. shellcheck:代码语法校验
  3. shell-format : 代码格式化
  4. Bash Debug 支持单步调试

    1. 首先要安装vscode插件
    2. 编写launch.json文件
    3. 升级bash到4.x以上的版本

Shell编程基础入门

Shell脚本和编程

1. 学习Shell的价值

  1. Linux服务器的基本操作和管理
  2. 前端node.js 服务的进程管理、问题排查、资源监控等运维操作
  3. 使用 shell 编写 TCE、SCM、Docker 脚本,完成服务编译和部署

    Linux很多指令都需要 Shell 完成

2. 学习环境依赖

  1. 一台安装了 Linux 系统的物理机或者云主机,可运行 Shell 脚本
  2. 本地的VScode 安装 Bash DeBug 插件,并且升级 bash 到 4.X 以上
  3. NPM全局安装 zx 依赖

3. Shell构成

shell官方对Shell的定义是命令行解释器,也是一门编程语言。

  • Shell 作为解释器

    • 可以解释脚本和命令
    • 同时bash 内置了命令
    • GNU 提供了很多工具,如: is 、mv、cat等
    • 第三方库:社区或自己编写的一些库,如:node、Python等封装的模块
  • Shell 作为编程语言

    • 变量:可以自定义变量、使用变量的能力
    • 运算:可以进行逻辑运算和算数运算
    • 语句:判断、分支、循环等编程语言具备的特点
    • 函数:定义函数和使用函数的能力

4. Hello Shell

  1. 在Linux中创建文件test.sh
  2. 在文件中编辑如下内容
#! /bin/bash  
echo "Hello Shell!"
  • 代码中第一行:#! 是一个约定的标记 表示告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 程序,人们并不区分 Bourne Shell 和 Bourne Again Shell,所以,像 #!/bin/sh,它同样也可以改为 #!/bin/bash
  • 第二行:echo 表示输出显示打印 引号内表示要输出的语句

5. 执行 Shell 脚本

要想执行Shell脚本,首先要看看对应的shell脚本文件是否拥有执行权限,可以使用 ll 命令查看,若没有执行权限,可以使用

chmod +x <shell脚本文件名>
  1. 执行脚本命令
# 中间不可以有空格
./<文件名.sh>
  1. 执行脚本命令二
sh <shell脚本文件名>

6. Shell的语法

1. Shell变量

69

2. declare [ + / - ] <选项> <变量>的参数

55

3. Shell自定义变量

# 变量名=变量值(等号左右不可以有空格,否则shell会当成一个命令)如:
name="张三"
age=21
score=100

# 将命令复制给变量
_ls=ls

# 将命令结果赋值给变量
files=$(ls -a)

# Shell默认的数据类型是字符串,如下面代码不会进行数学运算
num=age*score

# 数学运算需要声明变量为整型
let nums=age*score

declare -i nums=age*score

4. Shell变量的使用

在shell中使用 $ 符号对已经声明的变量进行使用

#! /bin/bash
size=2
page_num=3

let nums=size+page_num
echo "size="$size
echo "page_num="$page_num
echo "相加得"$nums
# 输出结果 
# size=2
# page_num=3
# 相加得5

5. 系统环境变量

69

系统变量可以直接在linux中进行使用直接使用如:echo $HOME

6. Shell 运算符

58

7. Shell 传递参数

在执行Shell脚本时,可以给Shell脚本传递参数,脚本内获取参数格式为$n 其中 n 代表参数传递过来的是第几个参数,如有下面shell脚本

#! /bin/bash

echo "第一个参数$0"
echo "第二个参数$1"
echo "第三个参数$2"
echo "第四个参数$3"

加入Shell脚本文件名为test.sh脚本执行的时候可以传参,传参方式如下:

sh test.sh 1 2 3 4
# 执行结果
第一个参数test2.sh
第二个参数1
第三个参数2
第四个参数3

总结:

我们会发现其中 $0 获取的是执行的shell文件名称,而自动忽略了第四个参数不显示,所以在Shell脚本中,若参数多的时候会忽略

8. Shell特殊字符处理参数

参数处理说明
$#传递到脚本的参数个数
$*以一个单字符串显示所有向脚本传递的参数。 如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。
$$脚本运行的当前进程ID号
$!后台运行的最后一个进程的ID号
$@与$*相同,但是使用时加引号,并在引号中返回每个参数。 如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
$-显示Shell使用的当前选项,与set命令功能相同。
$?显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

1.Shell脚本特殊字符处理参数($*)和($@)的区别

如:test.sh 文件中有如下代码

#!/bin/bash
echo "-- \$* 演示 ---"
for i in "$*"; do
    echo $i
done

echo "-- \$@ 演示 ---"
for i in "$@"; do
    echo $i
done

则输出结果

-- $* 演示 ---
1 2 3
-- $@ 演示 ---
1
2
3

我们可以从输出中看到$*会将传送的变量当作一个整体,而$@会将传输的参数散开,当成三个参数使用

7. Shell输入输出重定向

Shell 分为标准输入(fd0),标准输出(fd1),标准错误输出(fd2)

  • 输出重定向符号
符号含义
>覆盖式写入文件
>>追加写入文件
2>错误输出写入文件
&>正确和错误输出统一写入文件中
  • 输出重定向符号
符号含义
<
<<
需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。

8. Shell判断命令

  1. Shell 中提供了test[[[ 三种判断符号,可以使用到
  • 整数判断
  • 字符串测试
  • 文件测试
  1. 语法:
  • test <参数>
  • [ <参数> ]
  • [[ <参数> ]]
注意:
    中括号前后要有空格符
    [ 和 `test` 命令只能使用自己支持的标志位,<、>、= 只能用来比较字符串,不能比较整数
    中括号内的变量,最好都使用引号括起来
    [[ 更丰富,在整形比较中,支持<、>、= 在字符串中支持 =~ 正则
  1. 测试代码
#! /bin/bash
# 整数测试
test $n1 -eq $n2 # 两个整数变量等于
test $n1 -lt $n2 # 两个整数变量小于
test $n1 -gt $n2 # 两个整数变量大于

# 字符串测试
test -z $str # 判断字符串为空 -z
test -n $str # 判断字符串非空 -n
test $ str1 = $str2 # 判断字符串相等 =

# 文件测试
test -e /dmt && echo "exist" # 文件是否存在 -e
test -f /usr/bin/npm && echo "file exist" # 文件是否存在,并且是一个普通文件

9. Shell 条件判断

if <条件>
then
    代码块
elif <条件>
then
    代码块
else
    代码块
fi

判断两个变量是否相等

a=10
b=20
if [ $a == $b ]
then
   echo "a 等于 b"
elif [ $a -gt $b ]
then
   echo "a 大于 b"
elif [ $a -lt $b ]
then
   echo "a 小于 b"
else
   echo "没有符合的条件"
fi

如果使用 ((.....))

a=10
b=20
if (( $a == $b ))
then
   echo "a 等于 b"
elif (( $a > $b ))
then
   echo "a 大于 b"
elif (( $a < $b ))
then
   echo "a 小于 b"
else
   echo "没有符合的条件"
fi

10.Shell函数

  • 语法一
funName(){
    方法体
}
  • 语法二
function funName(){
    方法体
}

使用方法:

function add(){
    let sum=$1+$2
    echo sum
}
# 调用方式
* add 1 2
# 是不需要写括号的

在Shell中是不需要声明参数的,在方法内使用$进行参数获取

注意:

  • Shell 是自上而下执行的,函数定义在前,使用在后
  • 函数获取变量和 Shell script类似,$0 代表函数名称
  • 函数内return 仅仅表示函数的执行状态,不代表函数的执行结果
  • 返回结果一般使用echoprintf,在外边使用$()、``获取结果
  • 如果没有 return 函数的状态是上一条命令的执行状态,存储在$?

11. 模块化

模块化的原理是在当前的Shell内执行函数文件,引入方式:suorce [函数库的路径]

如,现在有文件test1.sh

#! /bin/bash
function add(){
    let sum=$1+$2
    echo $res
}

有第二个文件main.sh

#! /bin/bash
source './math.sh'
sum=$(add 1 2)
echo $sum

12. Shell脚本的执行过程

  1. 字符解析

    • 识别换行符,分号,做行的分割
    • 识别命令连接符(|| 、&&、 管道)做命令的分割
    • 识别空格,tab符号,做命令和参数的分割
  2. Shell 展开,如:{1..3} 解析为 1 2 3 后面会详细说
  3. 重定向,将标准文件的输入输出进行指向的变更
  4. 执行命令

    • 内置命令会在当前进程直接执行
    • 非内置命令会使用$PATH 查找,然后启动子进程执行
  5. 手机状态并返回

13. Shell 展开

1. 大括号展开

一般由三部分构成,前缀、一对大括号,后缀,大括号内可以是逗号分割的字符串序列,也可以是序列表达式

# 字符序列
a{b,c,d}e 展开结果=> abe ace ade
# 表达式序列 (数字可以使用..调整增量,字母不能调整增量)
{1..5}  =>  1 2 3 4 5

{1..5..2}  => 1 3 5

{a..e}   =>  a b c d e

2. 波浪号展开

波浪号会将当前替换成当前用户主目录的值

~  展开是 # $HOME就是用户目录 

~root  =>  # root指定用户的目录

~+    => # 当前工作目录

~-    =>  # 上一个工作目录

3. 大括号展开

  1. 间接参数扩展${!parameter},其中应用的并不是parameter 而是 parameter的实际值
parameter="var"
var="hello"
echo ${!parameter}
# 首先会将parameter转换成var
# 其次${var}的值是hello
# 所以最后输出的值是 hello
  1. 参数长度 ${parameter}

输出parameter的长度

par=cd
echo ${#par} 
# 输出 2
  1. 空参数处理

    1. ${parameter:-word} 为空替换 判断parameter的值是否为空,为空使用word替换
    2. ${parameter:=word} 为空替换,判断parameter的值是否为空,为空将parameter的值赋值为word
    3. ${parameter:?word} 为空报错
    4. ${parameter:+word} 不为空替换

如,有下面Shell脚本

a=1
echo ${a:-wrod} # 1

echo ${b:-wrod} # word

echo ${par:=wrod} # word

echo ${par:-hello} # word 因上一条语句已经赋值为word,所以不为空,所以输出结果还是word

echo ${a:+foo} # foo
  1. 参数切片

${parameter:offset}

${parameter:offset:lenth}

  1. 参数部分删除,对字符掐头去尾的操作

    1. ${parameter%word} # 最小限度从后面截取word
    2. ${parameter%%word} # 最大限度从后面截取word
    3. ${parameter#word} # 最小限度从前面截取word
    4. ${parameter##word} # 最大限度从前面截取word
#! /bin/sh
str=abcdefg

sp1=${str##*d}

sp2=${str%%d*}

echo $sp1 # 输出efg

echo $sp2 # 输出abc

14. Shell脚本VScode好用插件推荐

  1. Shellman :代码提示和自动补全
  2. shellcheck:代码语法校验
  3. shell-format : 代码格式化
  4. Bash Debug 支持单步调试

    1. 首先要安装vscode插件
    2. 编写launch.json文件
    3. 升级bash到4.x以上的版本

版权属于:都依凡 所有,采用《知识共享署名许可协议》进行许可,转载请注明文章来源。

本文链接: http://blog.anlucky.cn/index.php/programming/linux/59

赞 (3)

评论区(暂无评论)

啊哦,评论功能已关闭~