yanyue404/blog

Shell (Bash) 脚本编写实例

Opened this issue · 0 comments

开始

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/plugin-transform-runtime@7.12.15 @babel/preset-env@7.12.13 @babel/register@7.12.13 vue@3.2.9 vite@2.5.3

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/

参考链接