Shell脚本和编程 | 青训营笔记

66 阅读3分钟

Shell脚本和编程

学习shell的价值

在工作当中,或多或少会遇到需要使用shell的地方,所以学习shell是十分有必要的,shell可以用在一下三点

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

shell基础

0JEK7X1`24PK05JU9%}8%QU.png

shell首先是一个命令行解释器,其次它是一门编程语言.shell作为解释器可以解释执行脚本和命令,同时也提供了很多的内置命令,例如图中的"[","echo"等.shell作为一门编程语言,也提供了定义变量和使用变量的能力

作为编程语言的语法和命令

变量

OHAUOCOBCOV2VF8VS_3%ZZ4.png

自定义变量用"="声明,默认声明变量都是字符串型,整形、浮点型和日期型需要手动声明,作用域在当前shell

环境变量用"export"、"declare -x"声明,作用域在当前shell及其子shell

系统环境变量是在shell启动时加载进来,作用域在所有shell

父子shell

EKBE%3K84Z_HB}YG_0V{U9A.png

当父进程执行第三方或者核心工具时会启动子进程执行,执行完后交还给父进程,父进程继续执行下一条命令.在这过程中自定义变量只有在父进程中可以用到,环境变量和系统环境变量在父进程和子进程中都可以用到

自定义变量

# 变量名=变量值
page_size=1
page_num=2

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

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

# 声明变量为整形
let total=page_size*page_num

declare -i total=page_size*page_num

# 导出环境变量
export total

declare -x total

其中"="两边不能有空格,否则shell会当作额外命令.由于默认声明变量都是字符串,所以不会进行算数运算,声明变量为整形需要用到 let或者declare -i

declare基本选项
  • declare - 给变量设定类型属性
  • declare + 取消变量的类型属性
  • declare -a 将变量声明为数组类型
  • declare -i 将变量声明为整数型
  • declare -x 将变量声明为环境变量
  • declare- r 将变量声明为只读变量
  • declare -p 显示指定变量的被声明的类型

系统环境变量

1YVE2}JBFQ@7T%4R.png 环境变量可以通过vim ~/bashrc进行配置,配置完成后运行source ~/bashrc

运算符和引用

TA(PW1GLKDQZK8809Y5D7TC.png

管道

管道与管道符|,作用是将前一个命令的结果传递给后面的命令传递给后面的命令

语法cmd1|cmd2

要求: 管道右侧的命令必须能接受标准输入才行,比如grep命令,lsmv等不能直接使用,可以使用xargs预处理.xargs工作原理是将标准输入转化成参数列表的形式.

注意: 管道命令仅仅处理标准输出,对于标准错误输出会予以忽略,可以使用set -o pipefall设置shell遇到的管道错误退出

cat platform.access.log | grep ERROR 使用cat查看platform.access.log内容并将内容作为grep命令的输入

netstat -an | grep ESTABLISHED | wc -l使用netstat查看网络连接状态传递给grep,grep筛选后的结果再传递给wc命令做统计

重定向

SPGKLLD3WAAXPRK6RC2.png fd0指向终端输入,fd1指向终端输出,fd2指向终端输出,而重定向的作用就是修改fd1,fd2,fd3的指向,如下图所示

JP6QC.png 通过重定向,fd0指向了~./bashrc,fd1和fd2指向了tempfile

输出重定向符号

  • > : 覆盖写入文件
  • >> : 追加写入文件
  • 2> : 错误输出写入文件
  • &> : 正确和错误输出统一写入到文件中

输入重定向符号

  • <
  • <<

判断命令

shell中提供了test、[、[[三种判断符号,可用于:

  • 整数测试
  • 字符串测试
  • 文件测试

语法:

  • test condition
  • [condition]
  • [[condition]]

整数测试

  • test $n1 -eq $n2 判断n1是否等于n2
  • test $n1 -lt $n2 判断n1是否小于n2
  • test $n1 -gt $n2 判断n1是否大于n2

字符串测试

  • test -z $str_a字符串是否为空
  • test -n $str_a字符串是否非空
  • test $str_a = $str_b判断两个字符串是否相等

文件测试

  • test -e /dmt && echo "exist" 判断文件是否存在
  • test -f /usr/bin/npm && echo "file exist"判断文件是否存在并且是一个普通文件

注意:

  • 中括号前后要有空格符
  • [和test是命令,只能使用自己支持的标志位,<、>、=只能用来比较字符串
  • 中括号内的变量,最好都是用引号括起来
  • [[更丰富,在整形比较中支持<、>、=.在字符串比较中支持=~正则

分支语句

语法一:

if condition;then
    程序段
elif condition;then
    程序段
else
    程序段
fi

语法二:

case $变量 in:
    "第一个变量内容")
        程序段
        ;;
    "第二个变量内容")
        程序段
        ;;
        *)
        程序段
        ;;
    esac

其中if语句与我们熟悉的C,C++语言不同,if语句第一不需要小括号,其次if判断之后需要加上;之后是then,与python相同的是elif的写法,最后结束if语句是fi,正好是if倒过来的写法

case语句变量内容后要加上),程序段结束后要加上;;,最后case语句结束后要加上esac

循环

  • while循环
whlie condition ; do 程序段;done
  • until循环
until condition ; do 程序段; done
  • for循环
for var in [words...]; do 程序段; done

三种语句与C语言的循环语句有些类似,结尾需要加上done来结束循环语句

函数

语法一:

funcName(){ echo "abc";}

语法二

function funcName(){ echo "abc";}

函数调用时跟C语言不通,不需要小括号,后面参数用空格隔开,代表参数1,参数2.

注意:

  • shell自上而下执行,函数必须在使用前定义
  • 函数获取变量和shell script类似,$0代表函数名,舒徐参数通过$1,$2...获取
  • 函数内return与C语言中的不通,仅仅表示函数执行状态不代表函数执行结果
  • 返回结果一般使用echo、printf,在外面使用$()"获取结果
  • 如果没有return,函数状态时上一条命令的执行状态、存储在$?中

模块化

模块化的原理是在当前shell内执行函数文件,方式:

source [函数库的路径]

当我们修改完bashrc文件配置好环境变量后,直接运行命令是没有效果的,需要使用source ~/bashrc后才可以使用

常用命令

_8B[6JT]%T3B)OXL7Y1}IBO.png

执行

  1. shell脚本一般以.sh结尾,也可以没有.第一行需要指定用什么命令解释器来执行
#! /bin/bash
#! /usr/bin/env bash
  1. 启动方式
# 文件名运行
./filename.sh

# 解释器运行
bash ./filename.sh

#source运行
source ./filename.sh
  1. 执行过程
1. 字符解析
  - 识别换行符、分号(;) 做行的分割
  - 识别命令连接符(|| && 管道) 做命令的分割
  - 识别空格、tab符 做命令和参数的分割
2. shell展开
  - 大括号展开
      {a .. e} => a b c d e 
      {1..3} => 1 2 3
  - 波浪号展开
      当前用户目录: ~ => $HOME ~/foo => $HOME/foo
      指定用户目录 ~fred/foo => 用户fred的$HOME/foo
      当前工作目录 ~+/foo => $PWD/foo
      上一个工作目录 ~-/foo => $($OLDPWD-'~-')/foo
  - 参数展开
      间接参数扩展 ${!parameter}
      参数长度 ${#parameter}
      空参数处理
          ${parameter: -word} 为空替换
          ${parameter: =word} 为空替换,并将值赋给$parameter变量
          ${parameter: ?word} 为空报错
          ${parameter: +word} 不为空替换
      参数切片
          ${parameter:offset}
          ${parameter:offset:length}
      参数部分删除
          ${parameter%word}  最小限度从后面截取word
          ${parameter%%word} 最大限度从后面截取word
          ${parameter#word}  最小限度从前面截取word
          ${parameter##word} 最大限度从前面截取word
  - 命令替换
  - 数学计算 $((..))
  - 文件名展开 * ? [..] 外壳文件名模式匹配
3. 重定向,将stdin、stdout、stderr的文件描述符进行指向变更
4. 执行命令
  - builtin 直接执行
  - 非builtin 使用$PATH 查找,然后启动子进程执行
5. 收集状态并返回
  1. 调试
1. 普通log,使用echoprintf
2. 使用set命令
3. vscode debug插件

尾记

掌握shell语法可以让重复繁杂的工作变得些许的便利,例如在Linux系统中做项目需要多次运行一串代码,如果代码串很长,难免会有输错的可能,这时我们会按↑来回到这一条指令进行修改,多次的应用会带来或多或少的麻烦,但是将这串指令利用shell编写成脚本,我们只需要运行脚本这个文件,就可以完成指令的输入,会给项目带来许多便利.所以shell也需要多多接触,多加运用.