blog
blog copied to clipboard
Shell (Bash) 脚本编写实例
开始
Shell 的含义
学习 Bash,首先需要理解 Shell 是什么。Shell 这个单词的原意是“外壳”,跟 kernel(内核)相对应,比喻内核外面的一层,即用户跟内核交互的对话界面。
具体来说,Shell 这个词有多种含义。
首先,Shell 是一个程序,提供一个与用户对话的环境。这个环境只有一个命令提示符,让用户从键盘输入命令,所以又称为命令行环境(command line interface,简写为 CLI)。Shell 接收到用户输入的命令,将命令送入操作系统执行,并将结果返回给用户。本书中,除非特别指明,Shell 指的就是命令行环境。
其次,Shell 是一个命令解释器,解释用户输入的命令。它支持变量、条件判断、循环操作等语法,所以用户可以用 Shell 命令写出各种小程序,又称为脚本(script)。这些脚本都通过 Shell 的解释执行,而不通过编译。
最后,Shell 是一个工具箱,提供了各种小工具,供用户方便地使用操作系统的功能。
怎样在 windows 下使用 sh 命令?
借助 GIt,将 C:\Program Files\Git\bin (或计算机上的任何文件)放在 PATH 中,则可以使用,因为 Git 在其中放置了 包括 sh.exe 在内的各种有用的工具。
用户可以通过 bash 命令的--version 参数或者环境变量$BASH_VERSION,查看本机的 Bash 版本。
$ bash --version
GNU bash, version 4.4.23(1)-release (x86_64-pc-msys)
# 或者
$ echo $BASH_VERSION
4.4.23(1)-release
快捷键
Bash 提供很多快捷键,可以大大方便操作。下面是一些最常用的快捷键,完整的介绍参见《行操作》一章。
- Ctrl + L:清除屏幕并将当前行移到页面顶部。
- Ctrl + C:中止当前正在执行的命令。
- Shift + PageUp:向上滚动。
- Shift + PageDown:向下滚动。
- Ctrl + U:从光标位置删除到行首。
- Ctrl + K:从光标位置删除到行尾。
- Ctrl + W:删除光标位置前一个单词。
- Ctrl + D:关闭 Shell 会话。
- ↑,↓:浏览已执行命令的历史记录。
除了上面的快捷键,Bash 还具有自动补全功能。命令输入到一半的时候,可以按下 Tab 键,Bash 会自动完成剩下的部分。比如,输入 tou,然后按一下 Tab 键,Bash 会自动补上 ch。
除了命令的自动补全,Bash 还支持路径的自动补全。有时,需要输入很长的路径,这时只需要输入前面的部分,然后按下 Tab 键,就会自动补全后面的部分。如果有多个可能的选择,按两次 Tab 键,Bash 会显示所有选项,让你选择。
基本使用
# sh build.sh author by yanyue404
# 变量定义
_param1="Hello";
_param2="World!";
a=10
b=20
# 使用变量
echo $_param1
echo ${_param1}
# echo 传递参数
echo -e "\"Shell 传递参数实例!\"\n"
echo "参数个数为:$#";
echo "执行的文件名: $0"
echo "第一个参数为: $1"
echo "第二个参数为: $2"
echo "第三个参数为: $3"
# 双引号里可以有变量,双引号里可以出现转义字符
# 拼接字符串
echo "$_param1 ${_param2} $3"
# if 语句
if [ $a == $b ]
then
echo "a 等于 b"
fi
if [ $a != $b ]
then
echo "a 不等于 b"
fi
# if else 语句
if [[ $_param1 == $_param2 ]]
then
echo "相等的 command"
else
echo "不相等的 command"
fi
# if else-if else 语句
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
# 文件测试
if test -e ./README.md
then
echo "文件已存在!"
else
echo "文件不存在!"
fi
if test -d ./docs
then
echo "文件目录已存在!"
else
echo "文件目录不存在!"
fi
示例 Demo
自动打版本 tag
# git 项目自动标记 tag:
# 新增 tag: sh deploy-tag v1.0 feat: 备注信息
# 删除 tag: sh deploy-tag v1.0 --delete
if test -d .git
then
git pull
echo -e "\n# 项目 tag 版本信息:\n"
git tag
else
echo "该仓库 不是一个 git Repo !"
exit
fi
if [[ $1 && $# == 1 ]]
then
echo -e "\n# 新建 tag $1 并提交到远端:\n"
git tag $1
git push origin $1
elif [[ $1 && $2 != "--delete" ]]
then
echo -e "\n# 新建 tag $1 (附备注)并提交到远端:\n"
git tag -a $1 -m $2$3
git push origin $1
elif [[ $1 && $2 == "--delete" ]]
then
echo -e "\n# 删除本地及远程 tag $1: \n"
git tag -d $1
git push origin :refs/tags/$1
echo -e "\n# 更新后的项目 tag 版本信息:\n"
git tag
elif [[ $2 != "--delete" ]]
then
echo "输入的命令暂不支持。"
else
echo "请输入新建 tag 的版本号。"
fi
自动发布 tag js 版本
const { exec, echo } = require('shelljs')
const process = require('process')
/*
自动发布 tag
*/
// 支持传参,不用再修改此文件:npm run deploy:tag [newTag]
const [, , newTag] = process.argv
let publishTag = newTag || 'next-last'
exec('git pull')
echo(`# 删除本地 ${publishTag}`)
exec(`git tag -d ${publishTag}`)
echo('# 删除远程 tag')
exec(`git push origin :refs/tags/${publishTag}`)
echo(`# 新建 tag ${publishTag} 并提交到远端:`)
exec(`git tag ${publishTag}`)
exec(`git push origin ${publishTag}`)
测试与生产环境打包
echo "node -v"
node -v
_param=$1@second=$2@third@$3
cd TODO && dir
echo "build.sh 参数来了:"
echo -e "\n${_param}\n"
if [[ $1 == "dev" ]]; then
echo '开始编译测试环境'
cross-env PATH_TYPE=trial nuxt generate
# 执行测试打包命令
echo "{\"code\": 0, \"message\": \"编译成功\"}" >> ./build.log.json
exit
fi
if [[ $1 == "generate" ]]; then
echo '开始编译生产环境'
# 执行生产打包命令
npm -v
cross-env PATH_TYPE=production nuxt generate
exit
fi
echo '请指定编译模式 dev 或 generate'
build.js 传参编译
# sh build.sh @pageId=20240001@pd=@env=dev basepath/20240001 1634021630771
echo "npm install"
npm install
# sh build.sh $customParam $desUrl $timeStamp
_param=$1@BASE_URL=$2@JENKINS_TIME=$3
# buiuld.js params
echo "node build.js "${_param}
node build.js ${_param}
复制 git 仓库的某个文件夹
# sh build.sh
echo "克隆仓库..."
git clone -b dev http://gitlab.xxx.com/xxx.git .stash
echo "cp -rvf .stash/common/. .test"
cp -rvf .stash/common/. .test
echo "移除 .stash"
rm -rf .stash
克隆一个 git 仓库在本地进行打包
// 在 lib 目录 新建一个 libGitUrl git 仓库,并在本地进行打包
const path = require('path')
const fs = require('fs')
/**
*
* @param {*} path
*/
function getStat(path) {
return new Promise((resolve, reject) => {
fs.stat(path, (err, stats) => {
resolve(err ? false : stats)
})
})
}
/**
* 创建目录
* @param {*} dir
*/
function mkdir(dir) {
return new Promise((resolve) => {
fs.mkdir(dir, (err) => {
resolve(!err)
})
})
}
/**
* 判断目录是否存在
* @param {*} dir
*/
async function dirExists(dir, create = true) {
let isExists = await getStat(dir)
if (isExists && isExists.isDirectory()) {
return true
} else if (isExists) {
return false
}
// 不创建目录时直接返回
if (!create) {
return false
}
let tempDir = path.parse(dir).dir
let status = await dirExists(tempDir)
let mkdirStatus
if (status) {
mkdirStatus = await mkdir(dir)
}
return mkdirStatus
}
let libName = path.basename(libGitUrl, '.git')
await dirExists(path.resolve(__dirname, 'lib'))
shell.cd(path.resolve(__dirname, 'lib'))
if (!(await dirExists(path.resolve(__dirname, 'lib', libName), false))) {
shell.exec('git clone ' + libGitUrl)
}
shell.cd(libName)
shell.exec('git checkout --f')
shell.exec('git checkout ' + dev)
shell.exec('git pull')
console.log('node -v')
shell.exec('node -v')
console.log('pnpm -v')
shell.exec('pnpm -v')
console.log('pnpm install')
shell.exec('pnpm install')
console.log('pnpm build')
shell.exec('pnpm build')
shell.cd(path.resolve(__dirname, '../'))
将指定文件夹用做 dist 发布
echo "node -v"
node -v
echo "安装依赖"
npm install @babel/[email protected] @babel/[email protected] @babel/[email protected] [email protected] [email protected]
echo "开始 build"
vite build --config vite.config.vue3preview.js --outDir public/
# $? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误,如果不等于 0
if [ $? -ne 0 ]; then
echo "build 出错了"
exit 1
fi
echo "cp -r ./public/. ./dist/"
cp -r ./public/. ./dist/