Shell脚本的基本使用 | 青训营笔记

119 阅读2分钟

Shell脚本的基本使用

WHAT

command interpreter,处理来自终端模拟器的输入,解释执行之后输出结果给终端

而我们常说的Bash,则是Shell中的一种

WHY

学习shell,我们可以:

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

HOW

学习一门语言,我们从其最基本的组成和语法开始

变量及其操作

类型作用域声明方式规范
自定义变量当前shell=字符串、整型、浮点型、日期型
环境变量当前shell及其子shellexport、declare -x
系统环境变量所有shell启动加载
自定义变量

declare的选项:

    • 给变量设定类型属性
    • 取消变量的类型属性
  • -a 将变量声明为数组类型
  • -i 将变量声明为整数型
  • -x 将变量声明为环境变量
  • -r 将变量声明为只读变量
  • -p 显示指定变量的被生命的类型
# 变量名=变量值(注意等号左右不能有空格)
page_size=1
page_num=2# 将命令赋值给变量
_ls=ls
​
# 将命令结果赋值给变量
file_list=$(ls -a)
​
# 默认字符串,不会进行数学运算,所以下方代码是错误的
total=page_size*page_num
​
# 声明变量为整型
let total=page_size*page_num
​
declare -i total=page_size*page_num
​
# 导出环境变量
export total
​
declare -x total
​
系统环境变量
变量名含义常见操作
$0当前shell名称/脚本名称11、2等可以获取到传入参数
$#传入脚本的参数数量if [ $# -gt 1 ]
$*传入脚本的所有参数
$?上传命令执行的状态码if[ $? -eq 0 ];
$PS1命令提示符export PS1="\u@\h \w"
$HOME用户主文件夹cd ~
$PATH全局命令的搜索路径PATH=$PATH:[新增路径]

系统环境变量在初始化shell时在配置文件中读取,配置文件的加载流程如下:

confLoading.png

运算符和引用

operator.png

管道

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

语法:cmd1 | cmd2

但注意:管道右侧的命令必须能接受标准的输入才行,比如grep命令,ls、mv等不能直接使用,可以使用xargs预处理,且管道命令仅仅处理stdout,对于stderr会予以忽略,可以使用set -o pipefail设置shell遇到管道错误退出。

重定向

输出重定向:

: 覆盖写入文件

:追加写入文件

2> :错误输出写入文件

&>:正确和错误输出统一写入到文件中

输入重定向:

<

<<

判断命令

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

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

语法:

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

正常比较:

# 整数测试
test $n1 -eq $n2
test $n1 -lt $n2
test $n1 -gt $n2# 字符串测试
test -z $str_a
test -n $str_a
test $str_a = $str_b# 文件测试
test -e /dmt && echo "exist"
test -f /user/bin/npm && echo "file exist"

注意:

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

是用中括号:

name="hello world"
[ $name == "hello" ]

如上代码便会报错说:too many arguments

原因是$name会被直接解析为hello world两个变量,所以我们需要写成双引号将其包裹:

[ "$name" == "hello" ]

这时的"$name"才会被解析为"hello world"字符串

分支语句

语法1:

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

e.g.:

if [ "$yn" == "y" -o "$yn" == "Y" ]; then
    echo "ok continue"
fiif [ "$yn" == "y" ] || [ "$yn" == "Y" ]; then
    echo "ok continue too"
fi

语法2:

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

e.g.:

case $name in
    "nick")
        echo "hi nick"
    ;;
    "john")
        echo "my name is john"
    ;;
    *)
        echo "404"
    ;;
esac

循环语句

while循环:while condition ; do 程序段; done

e.g.:

let num=0
while [ $mum -lt 10 ]
do
    echo "current idx: $num"
    ((num++))
done

until循环:until condition ; do 程序段; done

e.g.:

let num=0
until [ $num -gt 10 ];
do
    echo "current idx: $num"
    ((num++))
done

for循环:for var in [words...]; do 程序段; done

e.g.:

# 对列表进行循环
for foo in a b c
do
    echo $foo
done# 数值方式循环
for((i=0;i<10;i++))
do
    echo $i
done

函数

语法一:funcName(){ echo "abc" }

e.g.:

printName() {
    if [ $# -lt 2 ]; then
        echo "illegal parameter"
        exit 1
    fi
    # $1和$2分别表示第一个和第二个参数,$0代表函数名
    echo "firstname is : $1"
    echo "lastname is : $2"
}
​
# 调用函数并传参
printName jacky chen

语法二:function funcName(){ echo "abc" }

e.g.:

function test() {
    # local定义局部变量,防止污染全局作用域
    local word="hello world"
    echo $word #返回结果hello world
    return 10
    # 使用unset来撤销变量
    unset word
}
​
content=`test` 
echo "状态码:$?" # $?为返回的状态10
echo "执行结果:$content" # content为执行结果hello world

注意:

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

模块化

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

source [函数库的路径]

function add() {
    declare -i res=$1+$2
    echo $res
}

👇

source './math.sh'
total=$(add 1 2)
echo $total

常用命令

commonCommand.png