-
Shell脚本
Shell 脚本(.sh文件)是一种用于自动化执行命令的脚本语言,常用于 Unix/Linux 系统。
-
基本语法 已经熟悉的可以跳过 可以忽略碎碎念学习其他文档
-
指定脚本解释器(必须放在第一行):
-
#!/bin/sh # 使用系统默认的 Shell(通常是 Bourne Shell) #!/bin/bash # 使用 Bash(功能更丰富)
-
-
单行注释
# 这是单行注释
- 变量
# 变量名区分大小写,无需声明类型:
name="Alice" # 定义变量(等号两侧不能有空格!)
echo "Hello $name" # 使用变量:输出 Hello Alice
# 环境变量
echo $PATH # 输出系统环境变量
export MY_VAR="value" # 将变量导出为环境变量
# 特殊变量:
echo $0 # 脚本名称
echo $1 # 第一个参数
echo $# # 参数个数
echo $@ # 所有参数
echo $$ # 当前进程ID
echo $? # 上一条命令的退出状态(0 表示成功)
- 输入输出
# 输出内容
echo "Hello World" # 普通输出
printf "Name: %s\n" $name # 格式化输出
# 读取输入
read -p "Enter your name: " name
echo "You entered: $name"
- 控制结构
# 条件判断 bash拓展为 [[]]
if [ $num -gt 10 ]; then
echo "大于 10"
elif [ $num -eq 10 ]; then
echo "等于 10"
else
echo "小于 10"
fi
# for 循环
for i in {1..5}; do
echo "Number: $i"
done
# while 循环
count=0
while [ $count -lt 5 ]; do
echo "Count: $count"
count=$((count + 1))
done
- 函数
# 函数的定义与调用
greet() {
echo "Hello, $1!"
}
greet "Bob" # 调用函数,输出 Hello, Bob!
# 返回值
is_even() {
if [ $(($1 % 2)) -eq 0 ]; then
return 0 # 偶数返回 0
else
return 1 # 奇数返回 1
fi
}
is_even 4 && echo "偶数" || echo "奇数"
- 文件操作与测试
# 文件存在性检查
if [ -f "/path/to/file.txt" ]; then
echo "文件存在"
fi
# 常见测试操作符
[ -d "/path" ] # 是否是目录
[ -x "/path" ] # 是否可执行
[ "$a" == "$b" ] # 字符串相等
- 错误处理
# 退出脚本
if [ ! -f "required.txt" ]; then
echo "文件不存在,退出!" >&2
exit 1 # 非零退出码表示错误
fi
# 忽略错误
command_that_might_fail || true # 即使失败也继续执行
-
常见操作符
==: 相等!=: 不相等-z: 字符串为空-n: 字符串不为空-f: 文件存在-d: 目录存在=~:正则表达式匹配
-
OSTYPE存储操作系统的名称
#! /bin/bash
echo "use OSTYPE:"
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
echo "Linux platform"
elif [[ "$OSTYPE" == "darwin"* ]]; then
echo "Mac platform"
elif [[ "$OSTYPE" == "cygwin" ]]; then
echo "Windows platform: Cygwin"
elif [[ "$OSTYPE" == "msys" ]]; then
echo "Windows platform: MinGW"
# elif [[ "$OSTYPE" == "win32" ]]; then # not sure
# echo "Windows platform"
elif [[ "$OSTYPE" == "freebsd"* ]]; then
echo "FreeBSD platform"
else
echo "Unknown platform: $OSTYPE"
fi
echo -e "\nuse uname:"
#os="`uname`" # os="$(uname)"
#echo "platform: ${os}" # echo "platform: $(uname)"
if [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
echo "Linux platform"
elif [ "$(uname)" == "Darwin" ]; then
echo "Mac platform"
elif [ "$(expr substr $(uname -s) 1 9)" == "CYGWIN_NT" ]; then
echo "Windows platform: Cygwin"
elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then
echo "Windows platform: MinGW 64"
else
echo "Unknown platform: $(uname)"
fi
echo -e "\nsystem architecture: $HOSTTYPE"
echo "test finish"
-
注意事项
-
引号使用:
- 单引号(
'):保持字符串字面值 - 双引号(
"):允许变量展开 - 反引号(`)或$():命令替换
- 单引号(
-
路径操作:
dirname: 获取路径的目录部分readlink: 读取符号链接的目标pwd: 当前工作目录
-
环境变量:
- 使用
export设置环境变量 - 环境变量通常大写
- 使用
-
命令执行:
command &: 后台执行command1 && command2: 前一个成功才执行后一个command1 || command2: 前一个失败才执行后一个
-
权限问题:执行脚本前需添加权限 ``
chmod +x script.sh -
调试脚本:使用 -x 参数跟踪执行:
bash -x script.sh
-
-
示例:code-cli.sh 使用Bash
#!/usr/bin/env bash # 使用 Bash(功能更丰富)
if [[ "$OSTYPE" == "darwin"* ]]; then
realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; }
ROOT=$(dirname $(dirname $(realpath "$0")))
else
ROOT=$(dirname $(dirname $(readlink -f $0)))
fi
function code() {
cd $ROOT
if [[ "$OSTYPE" == "darwin"* ]]; then
NAME=`node -p "require('./product.json').nameLong"`
CODE="./.build/electron/$NAME.app/Contents/MacOS/Electron"
else
NAME=`node -p "require('./product.json').applicationName"`
CODE=".build/electron/$NAME"
fi
# Get electron, compile, built-in extensions
if [[ -z "${VSCODE_SKIP_PRELAUNCH}" ]]; then
node build/lib/preLaunch.js
fi
# Manage built-in extensions
if [[ "$1" == "--builtin" ]]; then
exec "$CODE" build/builtin
return
fi
ELECTRON_RUN_AS_NODE=1 \
NODE_ENV=development \
VSCODE_DEV=1 \
ELECTRON_ENABLE_LOGGING=1 \
ELECTRON_ENABLE_STACK_DUMPING=1 \
"$CODE" --inspect=5874 "$ROOT/out/cli.js" . "$@"
}
code "$@"
此脚本解析
-
"$OSTYPE" == "darwin"*为 Mac platform-
- 是一个通配符,用于匹配任意字符序列 # 会匹配: darwin18.0 darwin19.0 darwin20.0 等等
- *
OSTYPE*存储操作系统的名称 前文有介绍
-
-
根目录确定
-
if [[ "$OSTYPE" == "darwin"* ]]; then realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } ROOT=$(dirname $(dirname $(realpath "$0"))) else ROOT=$(dirname $(dirname $(readlink -f $0))) fi - 根据操作系统类型(是否为macOS)使用不同的方式获取脚本的真实路径
- macOS没有内置的realpath命令,所以定义了一个realpath函数
- 最终得到VS Code项目的根目录路径
/*是一个模式,表示以斜杠 / 开头的任意字符序列[[ $1 = /* ]]判断第一个参数是否以斜杠开头,即是否是绝对路径#是字符串操作符,从开头删除匹配的模式- 所以
${1#./}会删除参数开头的./(如果存在的话) $PWD是绝对路径。它是一个环境变量,始终包含当前工作目录的完整绝对路径。- 其他系统使用
readlink -f命令获取符号链接的真实路径 - 用实例解释
-
# 假设当前目录是 /home/user # 例子1:绝对路径 realpath "/etc/passwd" # $1 = "/etc/passwd" # [[ "/etc/passwd" = /* ]] 为真,因为以 / 开头 # 所以输出 "/etc/passwd" # 例子2:相对路径 realpath "test.txt" # $1 = "test.txt" # [[ "test.txt" = /* ]] 为假,因为不以 / 开头 # 所以输出 "$PWD/test.txt" # 即输出 "/home/user/test.txt" # 例子3:以 ./ 开头的相对路径 realpath "./test.txt" # $1 = "./test.txt" # [[ "./test.txt" = /* ]] 为假,因为以 ./ 开头 # ${1#./} 去掉开头的 ./,变成 "test.txt" # 所以输出 "$PWD/test.txt" # 即输出 "/home/user/test.txt" $0是脚本本身的路径dirname命令用两次来获取上上级目录- 最终
ROOT变量包含项目的根目录路径
-
-
转入函数code,首先切换到项目根目录
-
可执行文件路径确定
-
if [[ "$OSTYPE" == "darwin"* ]]; then NAME=`node -p "require('./product.json').nameLong"` CODE="./.build/electron/$NAME.app/Contents/MacOS/Electron" else NAME=`node -p "require('./product.json').applicationName"` CODE=".build/electron/$NAME" fi - 根据操作系统类型设置不同的路径
- 使用 node 读取 product.json 中的配置
- macOS:使用 .app 包结构
- 其他系统:直接使用可执行文件
-
-
预启动处理
-
if [[ -z "${VSCODE_SKIP_PRELAUNCH}" ]]; then node build/lib/preLaunch.js fi -z检查变量是否为空- 如果 VSCODE_SKIP_PRELAUNCH 未设置,执行预启动脚本
-
-
内置扩展处理
-
if [[ "$1" == "--builtin" ]]; then exec "$CODE" build/builtin return fi - 检查第一个参数是否为 --builtin
- 如果是,执行内置扩展相关操作并返回
-
-
VS Code 启动
-
ELECTRON_RUN_AS_NODE=1 \ NODE_ENV=development \ VSCODE_DEV=1 \ ELECTRON_ENABLE_LOGGING=1 \ ELECTRON_ENABLE_STACK_DUMPING=1 \ "$CODE" --inspect=5874 "$ROOT/out/cli.js" . "$@" ELECTRON_RUN_AS_NODE=1:以 Node 模式运行 ElectronNODE_ENV=development:设置开发环境VSCODE_DEV=1:启用开发模式ELECTRON_ENABLE_LOGGING=1:启用日志记录ELECTRON_ENABLE_STACK_DUMPING=1:启用堆栈转储--inspect=5874:设置调试端口"$ROOT/out/cli.js":指定入口文件.:当前目录"$@":传递所有命令行参数
-
-
执行 code 函数
-
Windows系统自动化任务(.bat & .cmd)
-
基础语法
- 基本命令和语法:
@echo off关闭命令回显,通常放在批处理文件开头rem或::注释语句pause暂停执行并等待用户按键exit退出批处理程序
- 变量:
- 设置变量:
set variable=value - 使用变量:
%variable% - 命令行参数:
%1,%2等表示第一个、第二个参数 %~dp0表示当前批处理文件所在的目录路径
- 条件语句:
if condition (
command
) else (
command
)
- 循环语句:
for %%i in (item1 item2) do (
command
)
- 函数(标签):
:function_name
commands
goto :eof
调用函数:call :function_name
- 错误处理:
if errorlevel 1 (
rem 处理错误
)
- 常用操作符:
>输出重定向>>追加输出<输入重定向|管道&顺序执行多个命令&&仅在前一个命令成功时执行后面的命令||仅在前一个命令失败时执行后面的命令
- 实用命令:
cd切换目录mkdir/md创建目录del删除文件copy复制文件move移动文件dir列出目录内容
-
code-cli.bat
@echo off
setlocal
title VSCode Dev
pushd %~dp0..
:: Get electron, compile, built-in extensions
if "%VSCODE_SKIP_PRELAUNCH%"=="" node build/lib/preLaunch.js
for /f "tokens=2 delims=:," %%a in ('findstr /R /C:""nameShort":.*" product.json') do set NAMESHORT=%%~a
set NAMESHORT=%NAMESHORT: "=%
set NAMESHORT=%NAMESHORT:"=%.exe
set CODE=".build\electron%NAMESHORT%"
:: Manage built-in extensions
if "%~1"=="--builtin" goto builtin
:: Configuration
set ELECTRON_RUN_AS_NODE=1
set NODE_ENV=development
set VSCODE_DEV=1
set ELECTRON_ENABLE_LOGGING=1
set ELECTRON_ENABLE_STACK_DUMPING=1
:: Launch Code
%CODE% --inspect=5874 out\cli.js %~dp0.. %*
goto end
:builtin
%CODE% build/builtin
:end
popd
endlocal
@echo off
setlocal
title VSCode Dev
-
初始化设置
@echo off- 关闭命令回显setlocal- 创建局部变量环境,确保变量修改不会影响全局环境title VSCode Dev- 设置命令窗口标题
pushd %~dp0..
-
设置工作目录:
pushd保存当前目录并切换到新目录%~dp0获取脚本所在目录的路径..切换到上级目录
if "%VSCODE_SKIP_PRELAUNCH%"=="" node build/lib/preLaunch.js
-
预启动检查:
- 如果没有设置
VSCODE_SKIP_PRELAUNCH环境变量,则运行预启动脚本 - 预启动脚本可能包含获取 electron、编译代码、准备内置扩展等操作
- 如果没有设置
for /f "tokens=2 delims=:," %%a in ('findstr /R /C:""nameShort":.*" product.json') do set NAMESHORT=%%~a
set NAMESHORT=%NAMESHORT: "=%
set NAMESHORT=%NAMESHORT:"=%.exe
set CODE=".build\electron%NAMESHORT%"
//product.json
{
"nameShort": "Code - OSS",
"nameLong": "Code - OSS",
"applicationName": "code-oss",
"dataFolderName": ".vscode-oss",
"win32MutexName": "vscodeoss",
}
-
获取应用程序名称:
-
使用
findstr在product.json文件中查找 "nameShort" 字段-
findstr /R /C:""nameShort":.*" product.jsonfindstr:Windows系统下的文本搜索命令/R:使用正则表达式模式匹配/C:""nameShort":.*":搜索包含"nameShort":的行product.json:要搜索的文件- 将会找到以下行:
-
"nameShort": "Code - OSS"
-
for /f "tokens=2 delims=:," %%a-
for /f:这条命令启动一个循环,用于处理命令的输出。/f标志表示循环将从命令的输出中读取。 -
"tokens=2 delims=:,":这部分指定如何拆分每一行输入:tokens=2表示将捕获拆分后的第二个标记(或部分)。delims=:和,表示拆分行的分隔符是冒号(:)和逗号(,)。这在解析类似 JSON 的结构时非常有用。
-
-
do set NAMESHORT=%%~a- 将提取的值赋值给
NAMESHORT环境变量 %%~a会去除周围的引号"Code - OSS"=>Code - OSS
- 将提取的值赋值给
-
-
提取应用程序名称并处理字符串(移除引号和空格)
: ":这是一个字符串替换操作,表示要查找NAMESHORT中的所有引号和前面的空格(")。=%:表示将找到的引号和前面的空格替换为空字符串,也就是说,删除引号和前面的空格。
-
添加
.exe扩展名 -
设置完整的可执行文件路径
-
if "%~1"=="--builtin" goto builtin
-
内置扩展处理:
- 检查第一个命令行参数是否为
--builtin - 如果是,跳转到 builtin 标签处理内置扩展
- 检查第一个命令行参数是否为
set ELECTRON_RUN_AS_NODE=1
set NODE_ENV=development
set VSCODE_DEV=1
set ELECTRON_ENABLE_LOGGING=1
set ELECTRON_ENABLE_STACK_DUMPING=1
-
开发环境配置:
- 设置各种开发环境相关的环境变量
- 启用日志记录和堆栈转储
- 设置为开发模式
%CODE% --inspect=5874 out\cli.js %~dp0.. %*
goto end
-
启动 VS Code:
- 启动编辑器,启用调试端口 5874
%*传递所有命令行参数- 跳转到结束标签
:builtin
%CODE% build/builtin
-
内置扩展处理部分:
- 处理内置扩展的标签
- 使用特定参数启动编辑器
:builtin:这是一个标签,表示脚本中的一个跳转点。标签以冒号(:)开头,可以在脚本的其他地方通过goto命令跳转到这里。%CODE%:这是一个变量,通常在脚本的其他地方定义。它的值可能是一个命令或程序的路径,用于执行某个操作。build/builtin:这是传递给%CODE%命令的参数。它通常表示一个构建操作的目标或配置。
:end
popd
endlocal
-
清理和结束:
popd:这个命令用于返回到之前的目录。它与pushd命令配合使用,pushd用于保存当前目录并切换到新的目录,而popd则是恢复到之前保存的目录。这对于在脚本中处理多个目录时非常有用。endlocal:这个命令用于结束一个setlocal环境。setlocal命令用于创建一个局部环境,任何在此环境中所做的变量更改都不会影响全局环境。使用endlocal可以结束这个局部环境并恢复之前的环境设置。