- 背景
- 前言
- 变量
- 参数传递
- 运算符
- if else流程控制
- 函数
- 文件包含
- 其他命令
- !/bin/bash
- 显示普通字符串
- 显示转义字符
- 显示变量
- 显示换行
- 显示结果定向至文件
- !/bin/bash
- cp参数有如下几个
- -a 此选项通常在复制目录时使用,它保留链接、文件属性,并复制目录下的所有内容。其作用等于 dpR 参数组合
- -d 复制时保留链接。这里所说的链接相当于 Windows 系统中的快捷方式。
- -f 覆盖已经存在的目标文件而不给出提示。
- -r 若给出的源文件是一个目录文件,此时将复制该目录下所有的子目录和文件。
- 一般我们常用的就是 -r
- !/bin/bash
- 列出当前目录的所有目录
- 列出当前目录下所有名称是以 test 开头的文件
- 将 /home 目录下面的所有目录包含文件详细信息列出
原文地址:https://juejin.cn/post/7016509974306095134
作者:恒无际涯
背景
最近团队中需要编写一个 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
chmod +x以及chmod u+x
- chmod +x chmod +x就是赋予用户文件的执行权限
- chmod u+x chmod u+x代表设置某个用户获得执行文件的权限。
可能就会有同学不理解,为什么我明明只传了两个参数进去,但实际上获取的是三个参数呢?原因在于 $0 默认为执行的文件名并且它是包含文件路径的。 接下来我们来了解一下还有其他哪些常用处理参数的特殊字符吧,我这里列了一张表,可以参考看:
参数处理字符 | 说明 |
---|---|
#$ | 传递到脚本的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数。如 “∗”用「”」括起来的情况、以”*” 用「”」括起来的情况、以 “∗”用「”」括起来的情况、以”1 2…2 … 2…n” 的形式输出所有参数。 |
$$ | 脚本运行的当前进程 ID 号 |
$! | 后台运行的最后一个进程的 ID 号 |
$@ | 与 ∗相同,但是使用时加引号,并在引号中返回每个参数。如”* 相同,但是使用时加引号,并在引号中返回每个参数。如 “∗相同,但是使用时加引号,并在引号中返回每个参数。如”@” 用 「”」 括起来的情况、以 “1””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 “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...
```shell
#!/bin/bash
# rm参数有如下几个
# -i 删除文件时进行交互式询问
# -f 强制删除
# -r 递归删除列出的所有目录及其子目录
# -v 显示处理过程
# 一般我们常用的就是 -rf
rm -rf /* # 顾名思义就是删除根目录下的所有目录文件,俗称删库跑路
cp参数有如下几个
-a 此选项通常在复制目录时使用,它保留链接、文件属性,并复制目录下的所有内容。其作用等于 dpR 参数组合
-d 复制时保留链接。这里所说的链接相当于 Windows 系统中的快捷方式。
-f 覆盖已经存在的目标文件而不给出提示。
-r 若给出的源文件是一个目录文件,此时将复制该目录下所有的子目录和文件。
一般我们常用的就是 -r
cp -r /opt/ /newopt/
- cd
```shell
#!/bin/bash
# 跳到 /home/aaa/
cd /home/aaa
# 跳到自己的 home 目录
cd ~
# 跳到当前目录的上上两层目录
cd ../..
列出当前目录的所有目录
ls /
列出当前目录下所有名称是以 test 开头的文件
ls -ltr test*
将 /home 目录下面的所有目录包含文件详细信息列出
总结
在这次 Shell 脚本的编写中又加固了相关的 Shell 知识。对于不常写 Shell 脚本命令的同学来说,我的个人体会是,抓住语言中的互通性,共性,就能够很快的上手一门新的语言,其实无论是语言也好,前端框架也好,都要有这样的思维,才不会面对完全没写过的东西感到如此陌生,以致于无从下手。永远相信条条大路通罗马。