Windows&Linux-脚本开发-Bat&Shell
Windows&Linux-脚本开发-Bat&Shell
在日常的运维和开发工作中,我们经常需要重复执行一系列命令,比如每天备份日志、批量重命名文件、定时检查服务状态。手动敲命令不仅枯燥,还容易出错。脚本开发就是为了解决这个痛点——把一系列命令写进一个文件,交给系统一次性执行,实现任务的自动化。如果把操作系统比作一个工厂,命令行解释器(比如 Windows 的 cmd 和 Linux 的 bash)就是厂长,而脚本就是一份预先写好的生产流程单,厂长按单指挥工人(系统命令)干活。今天我们就从零开始,分别介绍 Windows 下的批处理(Bat)和 Linux 下的 Shell 脚本,让你能在两个平台上都写出简单实用的自动化脚本。
首先来看它们在整个系统中的位置。Windows 和 Linux 都是分层架构:最底层是硬件,上面是内核(Kernel),再往上是用户态的系统程序,包括我们最常用的命令行解释器。Bat 脚本由 Windows 的命令提示符(cmd.exe)解释执行,Shell 脚本则由 Linux 的 Shell(如 Bash、Zsh)解释执行。这些解释器本身也是用户态程序,它们读取脚本文件,把每一行文本解析成命令,然后通过系统调用让内核去完成实际的工作(比如读写文件、创建进程)。所以脚本位于用户态的最外层,是用户与内核交互的便捷桥梁。
下面我们用一个简单的流程图来展示脚本被执行的完整过程。
Mermaid 图表:脚本执行流程图

这张图里,最左边的“用户双击或输入脚本名”是触发动作,箭头指向“命令行解释器”,也就是脚本的执行引擎。解释器会先读取脚本文件,然后逐行解析命令。对于每一行,解释器会判断它是内部命令(比如 cd、set)还是外部程序(比如 copy、grep)。内部命令直接由解释器自身处理,外部程序则通过系统调用创建子进程来运行。执行完成后,输出或错误信息被收集并返回给终端,脚本接着处理下一行,直到结束。整个过程体现了脚本解释执行的本质——一边读一边做,没有编译环节。
现在我们来具体看看 Bat 和 Shell 各自的工作方式。Bat 脚本(扩展名 .bat 或 .cmd)是 cmd.exe 的命令集合。它为什么这样设计?因为早期 DOS 时代需要一种简单的方式来批量执行命令,所以设计了一套基本的控制语句(如 if、for)和变量机制,可以直接调用 Windows 下的各种 exe 程序。Shell 脚本则是 Unix/Linux 世界的产物,基于 Bourne Shell 语法,功能更强大,支持更复杂的表达式、函数、数组等,并且能与 Linux 丰富的命令行工具(如 grep、awk、sed)无缝结合。
实际中最常用的编写工具,Windows 下可以用自带的记事本,或者 Visual Studio Code(安装 Bat 插件)。运行方式有两种:一是直接双击脚本文件,二是打开 cmd 窗口,输入脚本路径回车。Linux 下常用 vim、nano 或 VS Code 编辑,运行前需要给脚本添加可执行权限(chmod +x script.sh),然后用 ./script.sh 执行,或者直接用 bash script.sh 运行。对比来说,Windows 的 Bat 语法相对简陋,对空格、特殊字符敏感;而 Shell 脚本语法更丰富,但也要注意不同 Shell(bash、sh、dash)之间的差异。
接下来我们用一个典型的真实场景来展示两种脚本的写法:每天凌晨备份某个文件夹下的所有日志文件,只保留最近 7 天的备份。这个任务在 Windows 和 Linux 下都可以用脚本完成。
先看 Windows 下的 Bat 脚本示例(backup_logs.bat):
@echo off
setlocal enabledelayedexpansion
:: 设置源文件夹和目标备份根目录
set source_dir=C:\logs
set backup_root=D:\backups
:: 生成带日期的备份子目录名,格式YYYYMMDD
for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /value') do set datetime=%%I
set date_str=%datetime:~0,8%
:: 完整备份路径
set backup_dir=%backup_root%\%date_str%
:: 如果备份目录不存在则创建
if not exist "%backup_dir%" mkdir "%backup_dir%"
:: 使用 robocopy 镜像复制(/MIR会删除目标中源没有的文件,这里只复制新增和修改过的)
robocopy "%source_dir%" "%backup_dir%" /E /R:1 /W:1
:: 删除7天前的备份文件夹
for /d %%D in (%backup_root%\*) do (
set folder_name=%%~nD
set folder_date=!folder_name!
:: 计算当前日期与文件夹日期的差值(简单比较字符串)
if "!folder_date!" lss "%date_str%" (
echo 删除旧备份: %%D
rmdir /s /q "%%D"
)
)
echo 备份完成。
endlocal
逐行解释:第一行 @echo off 关闭命令回显,让输出更干净;setlocal enabledelayedexpansion 启用延迟变量扩展,因为我们在循环中会修改变量。接着用 set 定义源目录和备份根目录。获取当前日期在 Windows 中稍微复杂,这里用 wmic 命令获取本地日期时间,再用 for 解析出纯日期字符串,截取前8位得到 YYYYMMDD。然后拼接出备份目录完整路径,如果不存在则用 mkdir 创建。核心备份命令是 robocopy,它比普通 copy 更可靠,/E 复制子目录及空目录,/R:1 /W:1 表示失败后重试1次、等待1秒。最后一段删除7天前的备份:遍历备份根目录下的所有文件夹,用 %%~nD 取出文件夹名(就是日期字符串),然后与当前日期字符串比较(由于日期格式是 YYYYMMDD,字符串比较正好能反映先后),如果小于当前日期,说明是7天前的(这里简化了判断,实际应该精确计算7天,但为演示只做小于判断),然后用 rmdir /s /q 强制删除。最后输出完成。
再来看 Linux 下的 Shell 脚本(backup_logs.sh):
#!/bin/bash
# 备份日志,保留最近7天
source_dir="/var/log/myapp"
backup_root="/backup/logs"
date_str=$(date +%Y%m%d)
backup_dir="${backup_root}/${date_str}"
# 创建备份目录
mkdir -p "$backup_dir"
# 使用 rsync 复制日志文件(归档模式,保持属性)
rsync -av "$source_dir/" "$backup_dir/"
# 删除7天前的备份
find "$backup_root" -maxdepth 1 -type d -name "20[0-9][0-9][0-1][0-9][0-3][0-9]" | while read old_dir; do
dir_date=$(basename "$old_dir")
# 如果目录名是8位数字且小于等于7天前,则删除
if [[ "$dir_date" =~ ^[0-9]{8}$ ]] && [[ "$dir_date" -le $(date -d "7 days ago" +%Y%m%d) ]]; then
echo "删除旧备份: $old_dir"
rm -rf "$old_dir"
fi
done
echo "备份完成。"
第一行 #!/bin/bash 称为 shebang,告诉系统用哪个解释器执行。然后定义变量,date_str 通过 date +%Y%m%d 直接得到。mkdir -p 确保父目录存在并创建目标。备份使用 rsync -av,-a 归档模式保持权限时间等,-v 显示过程。删除部分:用 find 查找备份根目录下所有符合日期格式的文件夹(-type d 且名字是8位数字),然后用 while read 循环处理每个文件夹。在循环里,用 basename 取出纯目录名,通过正则匹配确保是日期格式,再用 -le 比较数字大小(日期数字可以直接比较),如果小于等于7天前的日期,则删除。最后输出完成。
这两个脚本都体现了核心思想:利用系统自带的强大命令(robocopy/rsync)完成文件操作,加上简单的控制逻辑,实现自动化。容易踩的坑有哪些?对于 Bat 脚本,常见问题包括:路径中有空格未加引号导致命令解析错误;变量延迟扩展没开启导致循环中变量不更新;wmic 命令输出在不同语言系统上可能有差异。正确做法是统一用英文系统,或采用更稳定的 %date% 解析(但格式依赖区域设置)。验证方法:先手动执行单条命令,确认无误再写进脚本;在 cmd 中直接运行脚本观察输出。下一步操作建议:可以把脚本加入 Windows 任务计划程序,实现定时执行。
对于 Shell 脚本,常见坑点:脚本开头没有 #!/bin/bash 导致在某些环境下用 sh 执行,语法不支持;变量赋值等号两边不能有空格;if 里的条件写法(方括号前后要有空格);find 的结果可能包含特殊字符导致循环出错。正确做法是用 shellcheck 工具检查语法;测试时用 bash -x script.sh 查看详细执行过程。验证方法同样先手动运行命令,再执行脚本。下一步是使用 crontab 配置定时任务。
最后给出本模块的决策指南:什么时候必须用 Bat 或 Shell 脚本?当任务主要是调用现有命令行工具、处理文件、管理进程,且对性能和数据结构要求不高时,脚本是最快捷的选择。Windows 环境优先考虑 Bat,但如果需要更复杂逻辑(如正则、对象操作),可以改用 PowerShell;Linux 环境优先用 Bash 脚本。替代方案够用的情况:如果脚本超过 200 行,或者需要复杂的数据结构、异常处理、跨平台兼容,就应该考虑使用 Python 或 Go 等更高级的语言。对比而言,脚本语言的优势是无需安装额外环境,直接利用系统命令;缺点是调试不便、功能局限。对于今天的主题,记住 Bat 和 Shell 都是“胶水”语言,它们的价值在于把强大的系统命令粘合起来,让自动化变得简单。
Windows&Linux-权限差异-文件&用户组
见过太多因为搞不清Windows和Linux权限逻辑而导致的“惨案”:比如在Linux服务器上给了一个文件777的权限,或者试图在Windows上用管理Linux的思路去解决访问被拒的问题。这两种操作系统的权限模型,看似都在解决“谁对什么文件能做什么”的问题,但它们的底层逻辑和设计哲学截然不同。理解这种差异,是你未来在混合环境(Hybrid Environment)中进行安全运维的第一块基石。
从一把钥匙和一套门禁卡说起
想象一下,你要进入一栋大楼。Windows的权限模型,更像是一套复杂的中央门禁系统。每个房间(文件)的门上都有一份详细的访客名单(访问控制列表,ACL)。你刷工作证(用户账户)进门时,门禁系统会拿着名单逐一核对:你的名字在不在名单上?给你的权限是“仅浏览”(读)、“可以修改房间布置”(写),还是“能把这个房间转租给别人”(完全控制)?而且,这个名单是每个门独立维护的,非常细致,但也可能很冗长。
而Linux的经典权限模型,则更像是一个“军事化分级管理”的宿舍楼。每个文件(房间)只关心三类人:主人(Owner)、同组成员(Group) 和 访客(Others)。主人是谁?就是创建这个文件的人。同组成员是谁?是和你属于同一个项目组的人。访客就是其他不相干的人。每个文件对这每一类人,只问三个问题:能看吗(r)?能改吗(w)?能进去执行里面的程序吗(x)?这套模型简洁、高效,但在处理复杂、精细的权限需求时,就显得有些捉襟见肘了 。
系统架构中的位置:权限模块如何协作
在系统架构中,无论Windows还是Linux,文件权限管理模块都位于操作系统内核(Kernel)的虚拟文件系统(VFS,Virtual File System)层与安全子系统之间。
当任何一个进程(比如你正在使用的记事本或bash shell)尝试访问一个文件时,操作流程是这样的:
- 发起请求:进程通过系统调用(System Call)告诉内核:“我要打开这个文件进行写入。”
- 身份令牌:内核会查看这个进程是以哪个用户身份运行的(在Windows中是Token,在Linux中是EUID,即有效用户ID)。
- 权限裁决:内核将请求(进程的用户身份 + 想要的操作“写”)传递给安全参考监视器(Security Reference Monitor,SRM)。
- 访问控制列表:安全参考监视器(SRM)会去读取目标文件的安全描述符(Security Descriptor,Windows)或inode中的权限位(Linux)。它会比较进程的身份、请求的操作和文件上记录的权限规则。
- 允许或拒绝:如果规则允许,内核就放行,让进程操作文件;如果拒绝,就返回一个“Permission Denied”的错误。
下面这张图清晰地展示了这个流程。
Mermaid 图表:文件访问权限检查流程图

这张图里,最关键的就是粉色的“安全参考监视器”模块,它是权限裁决的大脑。蓝色部分是它所需要的数据来源。箭头展示了请求的流动方向:从用户进程发起,经过内核封装身份,最后到达裁决中心,根据文件上的“规则”做出判断。这是一个标准的“请求-评估-响应”模型,无论是Windows还是Linux,本质流程都遵循这个逻辑,只是评估的具体规则和数据结构天差地别。
核心差异:为什么它们设计得不一样?
这种差异源于它们诞生的时代和使命不同。
Windows(特别是NTFS权限) 诞生于企业网络环境,它的设计目标是精细化和可管理性。它假设一个文件可能被成百上千个不同的用户和组以不同的方式访问。因此,它引入了自主访问控制列表(Discretionary Access Control List,DACL)。这个列表是一个“准入名单”,里面可以包含几十甚至上百条访问控制项(Access Control Entry,ACE),每项都指定了某个特定用户或特定组的具体权限(比如读取、写入、删除、修改权限等)。这种模型非常灵活,但缺点是当列表变得很长时,检查和排错会变得复杂 。
经典的Linux权限(POSIX权限) 则继承自Unix,诞生于多用户的大型机和小型机时代,目标是简洁和高效。它认为大部分场景下,权限需求可以简单地归纳为“文件所有者”、“同项目组成员”和“其他人”这三类。所以它采用了固定的9个权限位来记录权限,用一个 ls -l 命令就能一目了然。这种设计极其简洁,内核做权限检查时效率极高。但它无法解决“我想让用户A能读,用户B能写,但他们都属于同一个组”这种更细粒度的需求 。
为了弥补经典权限的不足,Linux后来引入了POSIX访问控制列表(ACL,Access Control List),通过 setfacl 和 getfacl 命令,让Linux也能实现类似Windows的精细权限控制,但这并非默认标准,且使用上稍显复杂 。
| 特性 | Windows (NTFS权限) | Linux (经典POSIX权限) |
|---|---|---|
| 核心模型 | 自主访问控制列表(DACL) | 9位权限位(UGO模型)+ 可扩展ACL |
| 粒度 | 极细。可针对任意用户、组设置任意组合的权限。 | 粗。默认只能针对所有者、组、其他人三类设置读(r)、写(w)、执行(x)。 |
| 权限继承 | 强大且复杂。子对象默认继承父对象的权限,可配置阻断继承。 | 简单。目录的SGID位可让新文件继承目录的组,但权限位不自动继承。 |
| 特殊权限 | 丰富的专用权限,如“遍历文件夹/执行文件”、“列出文件夹/读取数据”、“删除”等 。 | 仅有SUID、SGID、Sticky Bit三种特殊位,功能特定 。 |
| 设计哲学 | 灵活性至上,满足复杂企业级权限管理需求。 | 简洁性至上,遵循“一切皆文件”的哲学,追求高效和透明。 |
实战演练:从命令到场景
光看理论不过瘾,我们来实际操作一下。假设我们需要为一个项目创建一个共享目录,让开发者可以读写,让测试人员只能读,其他人无权限。
Linux环境(Ubuntu 24.04 + Bash)
我们先在Linux上尝试用经典的UGO方式实现。
# 1. 创建用户组和用户 (参考Red Hat官方用户管理指南)
sudo groupadd project-alpha
sudo useradd -m -G project-alpha dev_user1 # 创建开发者用户并加入组
sudo useradd -m -G project-alpha tester1 # 创建测试员用户并加入组
# 2. 创建共享目录并设置基础权限
sudo mkdir -p /srv/project-alpha/data
# 将目录的所属组改为 project-alpha
sudo chown -R root:project-alpha /srv/project-alpha/data
# 设置目录权限: 所有者(root)有全部权限(7),组(project-alpha)有读写执行(7),其他人无权限(0)
sudo chmod 770 /srv/project-alpha/data
# 解释: 770 的二进制是 111 111 000,对应 rwxrwx---
# 这样,dev_user1 和 tester1 都属于 project-alpha 组,他们都有权限进入并读写data目录。
# 但问题来了:我们无法区分开发者的“写”权限和测试员的“只读”权限。他们现在都有写权限。
# 本脚本基于Linux man pages (man chown, man chmod) 和 Ubuntu 24.04 实际测试
# 3. 验证方法
# 切换到 dev_user1,尝试创建文件 (应该成功)
sudo -u dev_user1 touch /srv/project-alpha/data/dev_file.txt
ls -l /srv/project-alpha/data/
# 切换到 tester1,尝试删除文件 (应该成功,因为tester1也有写权限)
sudo -u tester1 rm /srv/project-alpha/data/dev_file.txt
# 这将成功,显然不符合我们的安全需求。
网络安全视角:这个例子暴露了Linux经典UGO权限模型在复杂场景下的天然缺陷。它无法实现“组内再分层”的权限控制。如果强行使用,要么导致权限过宽(如本例),要么管理成本激增(为测试员单独创建一个组,并为目录设置两个组的权限,但这需要借助ACL或POSIX ACL来实现)。在实际安全运维中,必须认识到UGO模型的局限性,对于有多角色协作需求的目录,应考虑使用ACL作为补充。
Windows环境(Windows 11 25H2 + PowerShell 7+)
现在我们在Windows上,用PowerShell通过 icacls 命令行工具来实现同样的需求。这正体现了Windows的精细化管理思想 。
# 1. 创建本地用户和组 (需要以管理员身份运行PowerShell 7+)
# 创建用户 (参考Microsoft Learn本地账户文档)
$password = ConvertTo-SecureString "InitialPwd123!" -AsPlainText -Force
New-LocalUser "Dev_Alice" -Password $password -FullName "Developer Alice" -AccountNeverExpires
New-LocalUser "Tester_Bob" -Password $password -FullName "Tester Bob" -AccountNeverExpires
# 创建本地组
New-LocalGroup -Name "Project-Alpha-Devs" -Description "Developers for Project Alpha"
New-LocalGroup -Name "Project-Alpha-Testers" -Description "Testers for Project Alpha"
# 将用户添加到组
Add-LocalGroupMember -Group "Project-Alpha-Devs" -Member "Dev_Alice"
Add-LocalGroupMember -Group "Project-Alpha-Testers" -Member "Tester_Bob"
# 2. 创建共享目录并设置精细权限
New-Item -ItemType Directory -Path "C:\Shares\Project-Alpha\Data" -Force
# 使用 icacls 重置权限并赋予精细控制 (参考微软 icacls 官方文档)
# 首先,禁用继承并复制当前权限(简化操作,实际可能直接移除所有然后新增)
# 这里我们采用更清晰的方式:先授予特定权限
# 注意:必须保证 SYSTEM 和 Administrators 有完全控制权,否则系统可能出问题
# 授予开发者组:修改权限 (M) - 包含读取、写入、删除
icacls "C:\Shares\Project-Alpha\Data" /grant "Project-Alpha-Devs:(OI)(CI)M"
# 参数解释: (OI) - 对象继承,此权限应用到文件; (CI) - 容器继承,此权限应用到子文件夹; M - 修改权限
# 授予测试员组:读取和执行权限 (RX)
icacls "C:\Shares\Project-Alpha\Data" /grant "Project-Alpha-Testers:(OI)(CI)RX"
# 确保管理员和系统有完全控制权(通常默认就有,但显式添加更安全)
icacls "C:\Shares\Project-Alpha\Data" /grant "Administrators:(OI)(CI)F"
icacls "C:\Shares\Project-Alpha\Data" /grant "SYSTEM:(OI)(CI)F"
# 本脚本基于 Microsoft Learn 官方 PowerShell 和 icacls 文档,在 Windows 11 25H2 测试通过
网络安全视角:Windows的精细权限虽然强大,但也引入了风险。ACL(访问控制列表)可能变得异常臃肿,一个错误配置的拒绝(Deny)ACE(访问控制项)可能会意外阻断合法用户(因为拒绝优先级高于允许)。此外,权限继承虽然方便,但也可能导致权限“漂移”或过度扩散。最佳实践是定期使用 icacls 或 Get-Acl 配合PowerShell导出权限清单进行审计,遵循最小权限原则,并严格审查所有“完全控制”权限的授予。
决策指南:什么时候用什么?
理解了差异,我们就能做出更明智的决策。
- 什么时候必须用Windows的精细模型?
- 复杂共享场景:当一个文件夹需要被数十个不同角色(如财务、HR、销售实习生、外部审计)以不同权限访问时。你不可能用Linux的UGO模型来管理这种复杂关系。
- 与Active Directory(AD)域集成:在企业域环境中,Windows权限模型天然与AD(Active Directory)用户和组无缝集成,可以实现基于部门、职级的统一权限管理 。
- 需要精细的审计和合规性:某些法规(如SOX,即萨班斯-奥克斯利法案,HIPAA,即健康保险携带和责任法案)要求对谁在何时访问或修改了特定文件有详细记录,Windows的审计策略比原生Linux更易于配置和理解。
- 什么时候Linux的经典模型或ACL就够用了?
- Web服务器:对于一个典型的LAMP(Linux, Apache, MySQL, PHP)或LNMP(Linux, Nginx, MySQL, PHP)网站,通常只需要一个运行用户(如
www-data)对网站目录有读写权限,其他人只读。chown -R www-data:www-data /var/www/html && chmod 750 /var/www/html一行命令搞定。 - 个人开发机或小型团队:如果你和几个同事都是开发者,共享一个代码仓库,大家角色相同,那么创建一个共同的组,设置
770权限就是最简洁有效的方案 。 - 系统文件保护:保护系统二进制文件和配置文件,如
/bin、/etc,经典权限模型足以防止普通用户意外修改 。
- Web服务器:对于一个典型的LAMP(Linux, Apache, MySQL, PHP)或LNMP(Linux, Nginx, MySQL, PHP)网站,通常只需要一个运行用户(如
总结一下,没有绝对的好坏,只有是否合适。Windows选择了灵活性,代价是复杂性;Linux选择了简洁性,代价是初始功能集的局限。作为安全运维人员,你的任务就是根据业务需求,在“够用”和“安全”之间找到最佳平衡点。记住,理解这些底层逻辑,比单纯记住几个命令要重要得多。
参考与进一步阅读
- Microsoft Learn: 本地用户 (LocalUser) 模块文档 – 本文PowerShell创建本地用户的依据。
- Microsoft Learn: icacls 命令行工具文档 – 本文Windows权限设置示例的官方来源。
- Red Hat Customer Portal: Linux 用户和组概述 – 本文Linux用户和组管理命令的权威参考。
- The Linux Documentation Project: 文件权限与属性 – 深入理解Linux文件权限的基础知识。
- Ubuntu Server Guide: 用户和组管理 – Ubuntu环境下用户和组管理的实践指南。



