新闻详情

新闻详情

首页 / 资讯中心 / 详情

Shell 脚本语言(Bash/Sh)基础 与 应用

发布时间:2026/7/12 22:58:08
Shell 脚本语言(Bash/Sh)基础 与 应用
目录兼容性概念连接符wait vs sleepPID Process ID进程IDecho ≈ print#! vs #cp vs rsynccp -r 的“增量合并”指的只是结构上的合并rsync 的“增量传输”指的则是数据块层面的省 IO-n模拟diff检验合并参数删除为例目录常用举例实时日志未知环境运行脚本1. 免疫“无执行权限”报错最常见的原因2. 强行纠正“环境盲区”防止用错解释器3. 忽略脚本第一行的路径报错caddy屏蔽报错2/dev/null检查服务死活curl -I url替换 为什么这么写简短拆解️ 安全稳妥版推荐带自动备份写入末尾EOF全目录搜索隐藏文件/目录find-name *.log环境参数、变量多环境本地开发配置终端Gateway网关服务/stop入栈网关curl兼容性终端环境兼容性说明Linux(bash/sh/zsh)✅ 完全适用标准 Shell 环境macOS(zsh/bash)✅ 大部分适用部分命令参数略有差异如sed -i需要备份后缀Windows (CMD)❌ 不适用使用完全不同的命令dirvslsWindows (PowerShell)⚠️ 部分适用支持ls、cat等别名但底层机制不同Windows (WSL/Git Bash)✅ 适用模拟 Linux 环境Dockerfile⚠️ 有限适用仅RUN/CMD内可使用且取决于基础镜像的 ShellShell 命令语言bash/sh主要在Unix-like 系统Linux、macOS、WSL中通用Dockerfile是一种声明式配置语言内部嵌套使用 Shell 命令跨平台问题写脚本时需要考虑目标环境例如用python而不是python3这种差异如果想让 Shell 脚本在不同终端都适用建议使用 POSIX 标准语法避免 bash 特有特性或用Python等真正的跨平台语言概念man test查看完整文档Linux/Mac连接符符号名称执行条件 / 作用示例顺序/逻辑控制;或直接换行顺序执行符前者结束无论成败则执行后者cmd1 ; cmd2逻辑与前者成功才执行后者make make install||逻辑或前者失败才执行后者make || echo Failed后台/异步控制后台运行符前者后台运行不等待立即执行后者server client数据流控制|管道符把前者的输出作为后者的输入cat log.txt | grep error其他常用#注释符其后的内容为注释不执行# 这是注释\换行转义符将下一行拼接为同一命令echo hello \worldwait vs sleepsleep time #等time秒wait $PID # 等进程结束PID Process ID进程ID$$ # 当前 Shell 的 PID $! # 最后一个后台进程的 PID $? # 上一个命令的退出码0成功 ps aux | grep node # 可以看到所有进程名带node的 PID kill 12345 # 通过 PID 杀死进程 echo $! # 输出刚启动的进程 PIDecho≈ print输出内容默认到终端类似console.log或printecho Hello World # 输出: Hello Worldecho 错误信息 2 # 输出到错误流echo -n 不换行 # -n 参数不换行echo $VAR # 输出变量值#! vs #普通#开头的行 → 注释完全忽略第一行的#!→ Shebang告诉系统用哪个程序执行脚本#!/usr/bin/env zsh #指定zsh执行cp vs rsynccp 直接覆盖会占用大量的磁盘 I/O 吞吐量全新复制或者一次性cp效率更高rsync只传有变化的内容需要额外安装适合大量文件频繁更新cp -r的“增量合并”指的只是结构上的合并rm -rf /root/.hermes/skills/mi-docs \cp -r /opt/hermes/built-in-skills/mi-docs /root/.hermes/skills/当你执行cp -r sourcedir/ destdir/时如果destdir已经存在结构合并如果源目录有A、B文件目标目录有B、C文件。执行后目标目录会变成A、B、C。那个原本就有的C不会被删掉。从目录结构和结果来看这叫“合并”或“增量”。底层 IO关键在这里对于那个同名的B文件cp的底层绝对不是“增量传输”。不管B的内容有没有变cp都会无脑地把源端的B重新读一遍然后强行写过去、把旧的B完全覆盖掉。所以它在数据传输上是全量覆盖在目录结果上是同名覆盖、异名并存。rsync的“增量传输”指的则是数据块层面的省 IO同样是处理同名的B文件rsync会先看两边B的体积和时间sizemtime。如果一样rsync连 1 个字节都不会读写直接跳过如果不一样rsync才会去传那个变动的文件rsync -a --delete ${SRC}/${dir}/ ${DEST}/${dir}/rsyncRemoteSync远程 / 本地文件同步--delete 会删掉目标区独有的文件是rsync独有的参数--checksum算 MD5 / rolling checksum再决定是否传输更精确但对大目录会慢很多一般不加。-a代表Archive归档模式。它是一个“全能组合参数”等价于-rlptgoD作用是一键完美保留文件的所有原生属性。简而言之加了-a复制过去的文件会保持原样不退化递归复制所有子目录和文件。不变质保留原文件的修改时间mtime、权限rwx。不失真保留文件的所有者owner、所属组group以及软链接。它是rsync拷贝目录时最常用、最稳健的标配参数。# 1. 是否安装了 rsync推荐 command -v rsync echo 已安装 || echo 未安装 # 2. 看路径 which rsync # 3. 看版本能输出版本号就说明可用【通用】 rsync --version | head -1-n模拟测试不写盘rsync -avn --delete ...dry-run验证 --delete在目标建源没有的文件干跑应出现 deleting ...-a(Archive / 归档)全能拷贝。一键完美保留文件的所有原生属性递归、时间戳、权限等。-v(Verbose / 详细输出)话痨模式。让rsync把正在比对、传输、删除的每一个文件名和进度都清清楚楚地打印在屏幕上。-n(Dry-Run / 空跑验证)核心免死金牌。告诉系统只做“模拟演习”绝对不往磁盘写入任何数据也不删除任何文件。diff检验diff -rq /opt/hermes/built-in-skills/mi-docs /root/.hermes/skills/mi-docs它是用来快速肉眼验证两个文件夹是否已经完全 100% 对齐镜像一致的黄金命令。其中的-rq是两个参数的组合全称和中文示意如下1.-r——Recursive递归比对中文示意不仅比对当前目录下的文件还要钻进所有的子文件夹、孙子文件夹里把里面所有的深层文件全部揪出来逐一进行跨时空比对。2.-q——Quiet /Brief静默 / 仅报告差异中文示意这是最关键的参数。普通的diff会把两个文件里哪一行代码不一样、哪一个字符有错全部密密麻麻地打印出来刷屏。而加上-q后它会变得极其高冷“只告诉你结果不废话”。情况 A两边完全一致最完美的景象现象终端一片寂静什么都没有输出返回状态码为 0。代表含义恭喜你/opt和/root里的这两个技能目录文件一模一样rsync --delete极其成功情况 B目标区WFS多了源区没有的文件现象终端会打印Only in /root/.hermes/skills/mi-docs: ghost_file.js代表含义目标区有孤儿文件说明你的--delete没生效或者没跑。情况 C两边的同名文件内容被修改了现象终端会打印Files /opt/.../index.js and /root/.../index.js differ代表含义这两个同名文件的内容不一样代码被动过了。合并参数删除为例rm 文件 # 删一个文件rm -r 目录 # 递归删目录里面所有东西rm -f 文件 # 强制删不确认rm -rf 目录 # 强制递归删最常用的组合-rrecursive递归目录和里面所有内容都删-fforce强制不提示确认不存在也不报错目录写法含义什么时候用~/xxx从家目录开始不管当前在哪都能找到/xxx从根目录开始固定绝对路径xxx或./xxx从当前目录开始你已经cd进去了/是根目录整台机器的起点/root是root用户的桌面藏在根目录下面root 是管理员账号他的家恰好叫/root/ ← 根目录一切从这里开始├── root/ ← root用户的家~在root身份下就是这里├── home/│ ├── work/ ← work用户的家~在work身份下就是这里│ └── zhangsan/ ← zhangsan的家├── opt/ ← 装软件的├── tmp/ ← 临时文件└── var/ ← 日志路径英文标准全称一句话大白话定位你的项目影子/optOptional第三方独立大项目安装区代码、脚本堆放地/opt/hermes/镜像里的通用代码库/etcExtendedToolConfigurations扩展工具配置全局软件和系统的纯文本配置文件存放区/etc/caddy/caddy_config.json/binBinaries二进制文件系统和核心用户必用的基础命令可执行程序编译好的、可以直接运行的程序/bin/bash强行调用的 Bash 解释器/usr/binUserSystemResourcesBin后续安装的第三方软件的命令/程序主入口/usr/bin/caddyCaddy 的可执行二进制文件/rootRootHome超级管理员 root 用户的私人专属“家目录”/root/.hermes/WFS 云盘挂载的个性化区df命令的英文全称是disk free查看系统磁盘空间就像window查看C、D盘一样。默认的单位是字节KB很难人类阅读。所以在实际开发和运维中100% 的人都会加上-h参数(human-readable)Mounted on (挂载点)这个磁盘分区在 Linux 系统里对应的具体文件夹路径。Filesystem … Mounted onwfsxxx /root/.openclawwfs 持久化换ip也不会丢常用命令英文全称中文示意用法示例说明catConcatenate连接/查看文件cat file.txt查看文件内容或合并多个文件cdChange Directory切换目录cd /home/user进入指定目录grepGlobal Regular Expression Print全局正则匹配打印grep text file.txt在文件中搜索匹配的文本模式-An/-BnAfter/Before紧随前/后的 n 行Context 上下文grep -A5 DIRS /opt/openclaw/sh/custom.sh匹配到关键词后额外展示紧随前/后的 n 行llLong Listing (alias)详细列表ll列出文件详细信息权限、大小、时间等lsList Segments/List列出目录内容ls显示当前目录下的文件和文件夹ll -alLong Listing - All Long显示所有详细列表ll -a -l列出所有文件包括隐藏文件的详细信息pwdPrint Working Directory打印当前工作目录pwd显示当前所在的绝对路径mkdirMake Directory创建目录mkdir a/b/c创建新目录若目录已存在则报错mkdir -pMake Directory -Parents递归创建目录mkdir -p a/b/c自动创建路径中所有缺失的父目录若目录已存在则静默跳过cpCopy复制文件cp file1.txt file2.txt复制文件若目标已存在则覆盖不询问cp -rCopy - Recursive递归复制目录cp -r sourcedir/ destdir/复制整个目录包含所有子文件和子文件夹。若目标同名文件夹已存在采用“全量覆盖写入”同名文件无脑重写覆盖异名文件保留并存~Tilde当前登录用户的家目录cd ~代表当前用户的home目录如/root/home/用户名Redirect覆盖重定向echo a file把输出写入文件覆盖原内容Append Redirect追加重定向echo a file把输出追加到文件末尾不覆盖echoEcho输出字符串echo hello打印到屏幕配合重定向可写文件which命令/可执行程序的安装目录which gitwhich nodewhich mis举例实时日志tail -f /var/log/openclaw/out.logtail (尾巴)Linux 的标准命令默认用来查看一个文件的最后 10 行。-f (Follow / 跟随)核心参数。让命令不要结束而是变成阻塞监听状态。只想实时看带 ERROR报错的日志可以用管道符把它组合起来tail -f /var/log/openclaw/out.log | grep --line-buffered ERROR未知环境运行脚本/bin/bash /opt/hermes/scripts/custom.sh/bin/bash 显式指定用系统里的 Bash 解释器来强行读取并执行后面的 custom.sh 脚本运维防御性写法它主要用来解决以下 3 个痛点1. 免疫“无执行权限”报错最常见的原因在 Linux 中一个脚本要想直接通过./custom.sh运行它必须拥有可执行权限x权限。x的英文全称是 execute加上执行权限。表示Add添加 / 赋予。x代表eXecute执行 / 运行。如果发布代码时忘记执行chmod x custom.sh赋予可执行权限直接运行就会报Permission denied拒绝访问错误。破局如果前面加上了/bin/bash系统会认为正在运行的程序是 BashBash 本身绝对有权限custom.sh只是作为“文本参数”喂给 Bash 读取。这样哪怕custom.sh没有任何执行权限也能完美正常运行2. 强行纠正“环境盲区”防止用错解释器有时候系统默认的sh并不是bash例如在 Ubuntu/Debian 里默认的sh叫dash功能非常阉割。如果直接执行或者有些容器环境在调用时图省事用了sh custom.sh脚本里写的很多高级 Bash 语法比如DIRS(example)这种数组定义就会在运行时疯狂报错崩溃。破局前面死死写住/bin/bash就是不给系统任何瞎猜的机会必须、只能用最纯正的 Bash 去解析。3. 忽略脚本第一行的路径报错脚本第一行通常写着#!/usr/bin/env bash或者#!/bin/bash俗称 释伴 / Shebang。 如果这个脚本是从 Windows 上编辑过再传到 Linux 上的它的换行符会变成\r\n。这会导致 Linux 在解析第一行时误以为你想找一个叫bash\r的解释器从而报错Command not found。破局前面加上/bin/bash后系统会直接跳过并忽略脚本第一行的 Shebang 声明直接从第二行正式代码开始执行从而规避了这种隐蔽的换行符巨坑。caddychmod 600Linux 里最高级别的隐私保护权限锁之一常用于私钥、密码文件chmod x /usr/bin/caddy chmod x /etc/caddy/caddy.sh chmod 600 /etc/caddy/public.key6(42) 拥有者root拥有读4写2权限。第 1 个0 所属用户组没有任何权限。同组同事打不开、看不了。第 2 个0 系统其他路人没有任何权限。屏蔽报错2/dev/nullif判断ls命令的退出码找到至少一个目录 → 退出码0→then执行cp没找到任何目录 → 退出码非0→ 跳过cpif ls claw-docker/openclaw/skills/*/ 1/dev/null 21; then \ cp -r claw-docker/openclaw/skills/* /opt/openclaw/skills/; \ fi \ 等价于 cp -r claw-docker/openclaw/skills/* /opt/openclaw/skills/ 2/dev/null || truefi是if的结束标志1/dev/null 21的含义一句话解释把命令的所有输出包括正常信息和错误信息都扔进“黑洞”/dev/null屏幕上什么都不显示。逐部分拆解符号含义数字对应1标准输出stdout正常的命令输出2标准错误stderr错误提示/dev/null重定向到空设备丢弃-1引用标准输出的目标-分步理解1/dev/null # 把标准输出正常信息丢进 /dev/null屏幕看不到 21 # 把标准错误错误信息也重定向到标准输出的位置 # 而标准输出已经被丢进 /dev/null 了所以错误也进了 /dev/null最终效果无论命令执行成功还是失败屏幕上都干干净净。检查服务死活curl -I url-I HEAD 发送HTTP HEAD请求只拿头部信息替换在 Linux 终端中最标准、高效的命令是使用sed。你可以直接运行以下命令来完成原地修改直接修改文件内容sed -i s|https://openapi\.com|https://openapi.cn|g .openclaw/openclaw.json 为什么这么写简短拆解-i表示In-place原地修改。它会直接修改openclaw.json文件本身而不是仅仅把结果打印在屏幕上。s|旧内容|新内容|g这是sed的替换语法。这里使用了竖线|作为分隔符通常用/这样可以避免和网址里的//以及/发生冲突免去写一堆转义反斜杠的麻烦。gGlobal代表全局替换即如果一行里出现了多次全部都会被改掉。\.在旧内容中.点在正则表达式里代表任意字符所以用\.转义了一下确保只精准匹配点号本身。️ 安全稳妥版推荐带自动备份如果你担心改错或者想要留个后路可以加上一个备份后缀比如.bakBashsed -i.bak s|https://openapi\.com|https://openapi.cn|g .openclaw/openclaw.json运行后它会修改原文件同时在同目录下自动生成一个openclaw.json.bak的未修改备份确认无误后你手动删掉备份即可。写入末尾EOFsource /root/.hermes/.env cat /opt/hermes/channels/mi/.env EOF MI_ROBOT_ID${MI_ROBOT_ID} MI_APP_ID${MI_APP_ID} MI_SEC_KEY${MI_SEC_KEY} EOF这段代码的作用是“将环境变量写入配置文件”。它通过source读取系统变量然后使用一种叫做EOF的技术把这三个配置项精准地追加到mi插件的.env文件里。# 设置环境变量只在当前终端有效 export PROXY_MODEsocket # 验证 echo $PROXY_MODE # 输出 socket # 关掉终端重新打开 → 变量消失1.source /root/.hermes/.envsource也可以写成一个点.是 Shell 的内置命令意思是“加载并执行指定文件中的内容”。大白话/root/.hermes/.env这个文件里可能写着诸如ROBOT_ID123456这样的环境变量。这一句的作用就是把这些变量真正加载到当前的 Linux 会话中让后面的命令能够读取到它们。2.cat /opt/hermes/channels/mi/.env EOF这行命令包含了三个核心部分cat原本是查看文件的命令但配合后面的符号在这里变成了“接收输入并输出”。(追加重定向)代表“把内容粘到文件的最末尾”。如果文件不存在它会创建 EOF(Here Document 技术)标记开始3. 什么是EOFEOF是End Of File文件结束的缩写。它其实只是一个分界符Marker。在脚本里它的作用就像是一对双引号只不过双引号只能扩住“一行”字而 EOF ... EOF可以扩住“整整一大段、带换行的多行文字”。全目录搜索隐藏文件/目录find path -name *.log1. 从当前目录开始递归搜索所有.env文件最常用Bashfind . -name .env.代表从当前目录开始往下找。-name .env精准匹配文件名。因为find命令默认就会搜索隐藏文件和隐藏文件夹所以不需要额外加特殊参数。2. 搜索整个系统从根目录/开始全局搜索如果你想在整个容器或整台服务器里彻查需要从/开始。因为有些目录普通用户无权访问建议加上sudo或屏蔽报错信息# 写法 A使用 sudo 权限推荐 sudo find / -name .env # 写法 B如果是普通用户屏蔽掉无权限的报错提示将错误定向到空设备 find / -name .env 2/dev/null3. 如果你想顺便看看.env文件的具体路径和详细属性结合-ls参数可以像ls -l一样列出文件的权限、大小、修改时间以及所属用户Bashfind . -name .env -ls 进阶如果你想顺便搜索.env里面的内容如果你不仅想找到.env文件还想顺便看看里面有没有包含某个关键词比如MI可以配合grep这样写# 找到所有 .env 文件并搜索里面包含 MI 的行 find . -name .env -exec grep -H MI {} \;⚡ 现代替代方案使用fd工具如果系统安装了的话如果你的系统里安装了现代化的高级搜索工具fd通常比传统 find 快很多由于它默认会忽略隐藏文件你需要加上-H参数fd -H .env在 Node.js 项目中package.json里的scripts对象就是一个“快捷键映射表”。{ main: index.js, scripts: { start: node index.js, setup: node setup.js, report: node weeklyReport.js, sync: npm run setup npm run start npm run report },npm run start简写为npm start是一个快捷命令别名。npm 就会在后台执行真正的命令node index.js即运行项目根目录下的index.js文件。环境参数、变量多环境本地开发配置scripts: { dev:test: cross-env BUILD_ENVtest NODE_OPTIONS--openssl-legacy-provider webpack-dev-server --inline --progress --config build/webpack.dev.conf.js, }cross-env这是一个跨平台的工具。因为 Windows 和 Mac/Linux 设置环境变量的语法不一样用它能保证在所有电脑上都能成功设置后面的变量。BUILD_ENVtest / sandbox / prod唯一的变量运行在测试环境/沙箱环境/生产环境调对应环境的后端接口 API。”NODE_OPTIONS--openssl-legacy-provider这是一个兼容性补丁。说明你的项目较老比如 Webpack 3/4而你电脑的 Node.js 版本较新Node 17不加这一句项目会直接报错崩掉。webpack-dev-server --config ...这是真正干活的启动命令。利用 Webpack 启动一个本地开发服务器支持热更新代码改了浏览器自动刷新。终端Gateway网关服务run.sh.sh是 Shell 脚本Shell Script的标准文件扩展名。 不过需要说明的是虽然后缀是.sh但具体用什么 Shell 解释器执行取决于脚本内部第一行Shebang。比如你这个脚本的第一行是#!/usr/bin/env zsh这代表它实际上指定了用zsh来执行而不是最古老的sh或常见的bash。#!/usr/bin/env zsh # Load nvm export NVM_DIR$HOME/.nvm [ -s $NVM_DIR/nvm.sh ] \. $NVM_DIR/nvm.sh # 使用子 Shell 执行确保执行完或中断后回到原目录 cd ../../ nvm use 22 PNPM_CONFIG_BLOCK_EXOTIC_SUBDEPSfalse OPENCLAW_FORCE_BUILD1 PROJECT_MOCK_AUTH1 node scripts/run-node.mjs gateway[和]组合起来其实是一个判断命令等同于test命令如判断是否是目录/脚本-d代表Directory目录-s 代表Shell脚本加载环境寻找并加载 NVMNode Version Manager确保终端可以使用nvm命令。切换目录cd ../../让终端向上跳两级目录去到项目的根目录。切换 Node 版本nvm use 22自动把当前 Node.js 环境切换到大版本 22。注入环境变量并运行后面那一长串是大写字母的是环境变量用来临时改变程序的行为最后用node执行了核心脚本。PNPM_CONFIG_BLOCK_EXOTIC_SUBDEPSfalse允许 pnpm 安装或使用一些非标准的依赖项。OPENCLAW_FORCE_BUILD1强制重新编译/构建 OpenClaw可能涉及一些 C 原生模块或前端资源。PROJECT_MOCK_AUTH1开启“PROJECT”服务的 Mock模拟身份验证研发阶段不需要真的去走登录鉴权流程。node scripts/run-node.mjs gateway用 Node 运行run-node.mjs脚本并传入参数gateway正式把网关服务跑起来。/stop入栈网关curl跨域是浏览器内核特有的安全保护机制命令行数据传输工具curl不受 CORS限制。curl -s -X POST http://127.0.0.1:18789/api/v1/stop \-H Content-Type: application/json \-d {sessionKey:agent:main:meishi:channel:20230fc124e,reason:security_block}-s静音模式Silent关闭curl的标准错误输出stderr中的进度表和错误信息。-X POST指定请求方式Request Method默认GET-HHeader-dData
网站建设 高端定制 企业官网