阅读 2408

前端常用的shell脚本命令

本文作者:恒无际涯

背景

最近团队中需要编写一个 shell 脚本,用于前端增量模块打包使用,主要就是为了解决每次打全量包耗费的时间较长,能够根据 git 提交判断哪些模块需要进行打包。为了实现这一个需求,那咱们作为前端切图仔就不得不去学习一些常用的 shell 命令了。

前言

学习一门语言基本的大致思路是相通的,就跟我们学习 javascript 一样,要知道 shell 都会有哪些东西,比如:变量、参数传递、运算符、流程控制、函数、文件包含等等。接下来我会根据这次学习到的知识进行一一分享。

变量

在 shell 中也需要像咱们写 javascript 一样进行变量的定义,那么在 shell 的变量定义中都有哪些规则呢?我这里列了一下有如下几点:

  • 命名只能使用英文字母、数字和下划线,首字符不能是以数字开头的
  • 中间不能有空格,可以使用下划线 _
  • 不能使用标点符号
  • 不能使用 bash 里的保留字(可以使用 help 命令查看保留字)

举个栗子:

#符合规则变量
SHELL
A_B_C_D
_abcd
abc123

#无效的变量命名
?var=123
var*name=hwjy
复制代码

在说完变量的定义后,就要聊该如何使用这些变量了,在变量的使用过程中一般会有两种写法,分别是 ${SHELL}$SHELL 这两种,乍看之下这两种写法无非就是一个有花括号一个没有,那么这个花括号在这其中起的作用主要是什么呢?带着问题,咱们来看看下面这个栗子:

for lang in Ada Coffe Action Java; do
    echo "I am a $langScript engineer"
done
复制代码

从这个栗子中大家应该不难发现,此时如果 lang 变量没有加上花括号就会被程序识别为 langScript 是一个变量,这样子执行出来的结果就跟我们的预期不符合了,所以为了避免这种情况的发生,我们在使用变量的时候需要加上花括号,为的就是能够更好的帮助解释器识别变量的边界,这也是一个良好的编码习惯。

参数传递

在我们执行 Shell 脚本的时候都需要将一些初始化的参数传递到 Shell 脚本中,就比如我这次写的脚本就必须要传入 git 分支名,让程序知道我是打哪个分支的包,以及需要上传到平台中的哪个版本中,举个栗子:

我们先编写一个简单的 Shell 脚本

#!/bin/bash
# test.sh

echo "第一个参数为$0";
echo "第二个参数为$1";
echo "第三个参数为$2";

复制代码

然后给脚本设置可执行权限并执行脚本

chmod +x test.sh 
./test.sh hello word

#执行结果
第一个参数为./test.sh
第二个参数为hello
第三个参数为word
复制代码

这里可能就会有同学不理解,为什么我明明只传了两个参数进去,但实际上获取的是三个参数呢?原因在于 $0 默认为执行的文件名并且它是包含文件路径的。 接下来我们来了解一下还有其他哪些常用处理参数的特殊字符吧,我这里列了一张表,可以参考看:

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

运算符

无论在什么语言中都少不了一些运算,那么在 Shell 中它又包含了哪些运算符呢?在进行 Shell 脚本的编写过程中,发现常用的运算符有关系运算符、布尔运算符、字符串运算符、算数运算符,除此之外还有文件测试运算符。

这里主要跟大家聊一聊在使用运算符时需要注意的一些事情,举个栗子:

栗子一:

#!/bin/bash
value=`expr 1 + 1`
echo "两数之和为:${value}"
# 这里需要注意的地方在于表达式和运算符之间要有空格,并且完整的表达式要被 ` ` 包含在其中。
复制代码

栗子二:

#error
[${a} == ${b}]

#success
[ ${a} == ${b} ]

# 这里需要注意的是在进行条件表达式判断时,务必要将表达式放在方括号之间,并且要有空格,否则会报错。
复制代码

if else流程控制

在一个 Shell 脚本中经常能遇到流程控制,就比如这次脚本的编写中,就需要判断当前提交的代码是否有改动到哪个对应的 App 并将其收集起来,最后生成一个打包队列,将各个 App 打成一个完整的前端包。

在 Shell 中的流程控制跟以往我们写 JavaScript 有些许的不同,它的不同之处在于 Shell 的流程控制是不能为空的,意思就是如果 else 没有语句执行就不要写这个 else 流程。

常见的流程控制语法如下:

#!/bin/bash

# if语句
if condition
then
    command
fi

# if else语句
if condition
then
    commandIf
else
    commandElse
fi

# if else-if else语句
if conditionIf
then
    commandIf
elif conditionElif
then
    commandElif
else
    commandElse
fi

# for循环语句
for lang in js html css
do
    echo ${lang}
done

# while循环语句
while condition
do
    command
done
复制代码

函数

在脚本的编写中,常常会涉及到将公共的方法封装成一个函数,那么在 Shell 脚本中,函数的定义又是怎样的呢? 这里我们先看一下函数的一个定义格式:

#!/bin/bash

[ function ] funname [()]
{
    action;
    [return int;]
}
复制代码

在这里我们可以发现,其实 Shell 的函数定义与我们平时写的 javascript 函数差别不会特别大,基本上我们在写 javascript 函数该有的东西,在 Shell 中也会有。

因此在函数的定义上咱们就不多去赘述了,既然在函数的定义上我们能找到一些共性,那么在函数的调用上应该也是差不多的。这里我们简单的定义一个函数以及调用它,就可直接明了的理解了。

#!/bin/bash

function fun(){
    echo $1$2
}

fun hello world

复制代码

这里就很好的展示了函数的一个传参使用方式,但是这里需要注意的一点是,函数的参数获取不是无限制的一直 $1 $2 ... $100 这样子获取参数,在这里它会有一个限制,就是当获取的参数大于 10 时,就需要改变一下写法,由 $10 变为 ${10} ,否则无法获取到对应的参数。

文件包含

在 Shell 中的文件包含我们该怎么去理解呢?我们可以想象为我们写 Vue 时,经常写的 Mixins 一样,将一些常用的公共代码、函数方法作为一个独立的文件封装起来。在这次的脚本编写中,依赖的安装、打包编译、上传至管理平台这些操作都属于公共的操作,所以在这里使用文件包含恰恰符合使用场景。

Shell 的文件包含语法格式如下:

#!/bin/bash

. path/filename # 注意点号(.)和路径中间有一个空格
或者
source path/filename
复制代码

这样通过文件包含的形式促使我们脚本的灵活性,可以随时随地去使用到相关的函数方法,是一个不错的实践方式。

其他命令

  • echo字符串输出
#!/bin/bash

# 显示普通字符串
echo "hello world"

# 显示转义字符
echo "\"hello world\"" # 此时结果显示为"hello world"

# 显示变量
content="hello world"
echo "He said ${content}"

# 显示换行
echo -e "hello \n" # -e开启转义,即\n可以换行
echo "world"

# 显示结果定向至文件
echo "hello world > test"
复制代码
  • exit [code]

以退出码为 code 退出当前进程

  • rm [options] name...
#!/bin/bash

# rm参数有如下几个
# -i 删除文件时进行交互式询问
# -f 强制删除
# -r 递归删除列出的所有目录及其子目录
# -v 显示处理过程
# 一般我们常用的就是 -rf
rm -rf /* # 顾名思义就是删除根目录下的所有目录文件,俗称删库跑路
复制代码
  • cp [options] source dest
#!/bin/bash

# cp参数有如下几个
# -a 此选项通常在复制目录时使用,它保留链接、文件属性,并复制目录下的所有内容。其作用等于 dpR 参数组合
# -d 复制时保留链接。这里所说的链接相当于 Windows 系统中的快捷方式。
# -f 覆盖已经存在的目标文件而不给出提示。
# -r 若给出的源文件是一个目录文件,此时将复制该目录下所有的子目录和文件。
# 一般我们常用的就是 -r
cp -r /opt/ /newopt/
复制代码
  • cd
#!/bin/bash

# 跳到 /home/aaa/
cd /home/aaa

# 跳到自己的 home 目录
cd ~

# 跳到当前目录的上上两层目录
cd ../..

复制代码
  • ls
#!/bin/bash

# 列出当前目录的所有目录
ls /

# 列出当前目录下所有名称是以 test 开头的文件
ls -ltr test*

# 将 /home 目录下面的所有目录包含文件详细信息列出
ls -lR /home

复制代码

总结

在这次 Shell 脚本的编写中又加固了相关的 Shell 知识。对于不常写 Shell 脚本命令的同学来说,我的个人体会是,抓住语言中的互通性,共性,就能够很快的上手一门新的语言,其实无论是语言也好,前端框架也好,都要有这样的思维,才不会面对完全没写过的东西感到如此陌生,以致于无从下手。永远相信条条大路通罗马

文章分类
前端
文章标签