目标:仅当前工作目录读写,不允许碰系统/外部目录;普通用户运行,不用 sudo,避开--dangerously‑skip‑permissionsroot 报错
核心:不要 sudo,进入你的项目目录,创建项目本地配置
.claude/settings.local.json(加入.gitignore,不提交版本库)
1、在你的项目目录创建配置
mkdir-p.claudevim.claude/settings.local.json写入下面配置:
{"permissions":{"defaultMode":"acceptEdits","allow":["Read","Write","Glob","Grep"],"deny":["Bash(sudo *)","Bash(rm -rf / *)","Bash(curl *)","Bash(wget *)"],"additionalDirectories":[]},"sandbox":{"enabled":true,"filesystem":{"allowWrite":["./"],"denyRead":["//etc","//usr","//home/*/.ssh","//root","~/.*"]}}}defaultMode: acceptEdits:当前目录内文件读写自动放行,不再弹窗确认;shell 命令依然需要你确认additionalDirectories: []:禁止访问任何项目外目录sandbox.filesystem.allowWrite: ["./"]:OS 沙盒强制只允许写入当前目录,子进程也遵守该限制denyRead:禁止读取系统目录、ssh密钥等敏感路径
⚠️不能加 sudo 运行 claude,保持普通用户账号执行:
# 正确:进入项目目录,普通用户启动cd/your/project claude2、会话内快速切换(不用改配置文件)
进入 claude 交互终端后输入:
/permissions菜单选择:acceptEdits(接受编辑),效果:本会话内当前目录读写自动放行,shell命令仍询问。
3、如果你需要非交互脚本运行(无终端交互)
不能用--dangerously‑skip‑permissions,改用--permission-mode acceptEdits
claude --permission-mode acceptEdits-p"你的任务描述"限制:Bash shell 命令依然会阻塞等待确认,非交互脚本如果有 shell,会卡住。如果脚本需要自动跑 shell,要在
allow白名单显式添加允许的 bash 模式,例如"Bash(git *)"。
4、关键限制回顾
--dangerously‑skip‑permissions只要检测到 effective uid=0(root/sudo),直接硬报错,无解,官方安全锁死,不能绕过。acceptEdits≠ dangerously‑skip‑permissions:- ✅ acceptEdits:文件读写自动放行;shell命令仍然弹窗确认;沙盒目录限制生效
- ❌ dangerously‑skip‑permissions:文件+shell全部跳过确认,且禁止root下使用
5、如果你必须执行 shell 也自动放行(风险上升)
在allow增加 bash 通配,仅允许安全命令,禁止危险命令:
"allow":["Read","Write","Glob","Grep","Bash(git *)","Bash(npm *)","Bash(pytest *)"]排错
- 如果 claude 还试图访问外面文件:确认运行时 CWD 是你的项目目录,不要在
$HOME根目录运行。 - 检查配置是否生效:
claude debug settings会打印合并后的完整权限配置,确认 sandbox、permissions 读取的是你项目下.claude/settings.local.json。
完整配置(当前目录读写 + Git权限)
.claude/settings.local.json,务必加入.gitignore,普通用户运行 claude,不要 sudo。
两种方案:
方案A(推荐,安全优先)
常用git操作自动放行;push / merge / push‑force仍然弹窗确认,防止误推送代码到远程仓库。
{"permissions":{"defaultMode":"acceptEdits","allow":["Read","Write","Glob","Grep","Bash(git status *)","Bash(git diff *)","Bash(git log *)","Bash(git add *)","Bash(git commit *)","Bash(git stash *)","Bash(git branch *)","Bash(git checkout *)"],"ask":["Bash(git push *)","Bash(git merge *)","Bash(git rebase *)"],"deny":["Bash(git push --force *)","Bash(sudo *)","Bash(rm -rf *)","Bash(curl *)","Bash(wget *)","Read(.env)","Read(.env.*)"],"additionalDirectories":[]},"sandbox":{"enabled":true,"filesystem":{"allowWrite":["./"],"denyRead":["//etc","//usr","//home/*/.ssh","//root","~/.*"]}}}方案B(宽松,全部git自动放行)
⚠️风险:claude可以直接执行
git push,不会弹窗确认。
{"permissions":{"defaultMode":"acceptEdits","allow":["Read","Write","Glob","Grep","Bash(git *)"],"deny":["Bash(git push --force *)","Bash(sudo *)","Bash(rm -rf *)","Bash(curl *)","Bash(wget *)","Read(.env)","Read(.env.*)"],"additionalDirectories":[]},"sandbox":{"enabled":true,"filesystem":{"allowWrite":["./"],"denyRead":["//etc","//usr","//home/*/.ssh","//root","~/.*"]}}}关键要点
defaultMode: acceptEdits:当前目录文件读写自动放行,shell命令看allow/ask/deny规则,不等于--dangerously‑skip‑permissions。- 规则优先级:
deny>ask>allow> defaultMode,命中即停止匹配。 - sandbox强制限定只允许写入当前目录,claude无法读写项目外路径。
- 禁止sudo运行claude,否则
--dangerously‑skip‑permissions直接报错,这套配置不需要该参数。
验证配置是否生效
进入claude交互终端执行:
/config会打印合并后的完整权限规则,确认你的git规则被加载。
命令行临时一次性会话(不写配置文件)
claude --permission-mode acceptEdits--allowedTools"Read,Write,Glob,Grep,Bash(git *)"仅本次会话生效,退出失效。
.claude/settings.local.json是项目本地配置文件;想要等价效果全部放到命令行参数,claude CLI 通过--allowed-tools、--asked-tools、--denied-tools、--permission-mode实现。
注意:sandbox 文件系统限制目前没有命令行参数,只能靠配置文件,命令行只能控制 permissions 工具权限。
对应你这份 JSON 的完整命令行
claude\--permission-mode acceptEdits\--allowed-tools"Read,Write,Glob,Grep,Bash(git status *),Bash(git diff *),Bash(git log *),Bash(git add *),Bash(git commit *),Bash(git stash *),Bash(git branch *),Bash(git checkout *)"\--asked-tools"Bash(git push *),Bash(git merge *),Bash(git rebase *)"\--denied-tools"Bash(git push --force *),Bash(sudo *),Bash(rm -rf *),Bash(curl *),Bash(wget *),Read(.env),Read(.env.*)"重要限制
sandbox 块(
allowWrite/denyRead)没有 CLI 参数
命令行无法复现「只允许写当前目录、禁止读/etc、.ssh」这部分,必须依赖.claude/settings.local.json文件。
👉 如果你只跑上面这条命令,claude 依然可以读取项目外文件,少了沙盒隔离。additionalDirectories: []同样无命令行参数,配置文件专属。不要加
sudo,不要加--dangerously-skip-permissions。
简短封装成别名(放到 ~/.bashrc / ~/.zshrc)
aliasclaude-proj='claude \ --permission-mode acceptEdits \ --allowed-tools "Read,Write,Glob,Grep,Bash(git status *),Bash(git diff *),Bash(git log *),Bash(git add *),Bash(git commit *),Bash(git stash *),Bash(git branch *),Bash(git checkout *)" \ --asked-tools "Bash(git push *),Bash(git merge *),Bash(git rebase *)" \ --denied-tools "Bash(git push --force *),Bash(sudo *),Bash(rm -rf *),Bash(curl *),Bash(wget *),Read(.env),Read(.env.*)"'source 之后直接:
claude-proj最佳实践建议
- 权限工具白名单(allow/ask/deny):用命令行/别名,方便临时切换;
- 目录沙盒隔离(sandbox.filesystem):保留项目目录下
.claude/settings.local.json,命令行做不到。
调试查看最终生效工具列表:
claude debug tools如果你要做非交互一次性任务(-p 传入prompt):
claude --permission-mode acceptEdits\--allowed-tools"Read,Write,Glob,Grep,Bash(git status *),Bash(git diff *),Bash(git log *),Bash(git add *),Bash(git commit *),Bash(git stash *),Bash(git branch *),Bash(git checkout *)"\--asked-tools"Bash(git push *),Bash(git merge *),Bash(git rebase *)"\--denied-tools"Bash(git push --force *),Bash(sudo *),Bash(rm -rf *),Bash(curl *),Bash(wget *),Read(.env),Read(.env.*)"\-p"你的任务"⚠️ 非交互模式下命中--asked-tools(例如 git push)会阻塞等待输入,脚本场景避开会触发 ask 的操作。
用jq一行命令直接生成/覆盖.claude/settings.local.json,输出完整你上面那套 JSON,单条 shell 语句,直接写入文件。
cat
mkdir-p.claude&&cat>.claude/settings.local.json<<'EOF' { "permissions": { "defaultMode": "acceptEdits", "allow": ["Read","Write","Glob","Grep","Bash(git status *)","Bash(git diff *)","Bash(git log *)","Bash(git add *)","Bash(git commit *)","Bash(git stash *)","Bash(git branch *)","Bash(git checkout *)"], "ask": ["Bash(git push *)","Bash(git merge *)","Bash(git rebase *)"], "deny": ["Bash(git push --force *)","Bash(sudo *)","Bash(rm -rf *)","Bash(curl *)","Bash(wget *)","Read(.env)","Read(.env.*)"], "additionalDirectories": [] }, "sandbox": { "enabled": true, "filesystem": { "allowWrite": ["./"], "denyRead": ["//etc","//usr","//home/*/.ssh","//root","~/.*"] } } } EOF或者需要先安装 jq:
apt install jq/brew install jq
mkdir-p.claude&&jq-n'{ "permissions": { "defaultMode": "acceptEdits", "allow": ["Read","Write","Glob","Grep","Bash(git status *)","Bash(git diff *)","Bash(git log *)","Bash(git add *)","Bash(git commit *)","Bash(git stash *)","Bash(git branch *)","Bash(git checkout *)"], "ask": ["Bash(git push *)","Bash(git merge *)","Bash(git rebase *)"], "deny": ["Bash(git push --force *)","Bash(sudo *)","Bash(rm -rf *)","Bash(curl *)","Bash(wget *)","Read(.env)","Read(.env.*)"], "additionalDirectories": [] }, "sandbox": { "enabled": true, "filesystem": { "allowWrite": ["./"], "denyRead": ["//etc","//usr","//home/*/.ssh","//root","~/.*"] } } }'>.claude/settings.local.json执行完直接落地文件,无需手动编辑。
校验是否写对
cat.claude/settings.local.json如果想追加到 .gitignore(一并一行)
echo".claude/settings.local.json">>.gitignore如果你是想修改已有json,而不是覆盖
覆盖是上面;如果已有 settings.local.json,只想更新权限字段,用这个(不会清空其他已有key)
mkdir-p.claude&&jq'. |= ( .permissions.defaultMode = "acceptEdits" | .permissions.allow = ["Read","Write","Glob","Grep","Bash(git status *)","Bash(git diff *)","Bash(git log *)","Bash(git add *)","Bash(git commit *)","Bash(git stash *)","Bash(git branch *)","Bash(git checkout *)"] | .permissions.ask = ["Bash(git push *)","Bash(git merge *)","Bash(git rebase *)"] | .permissions.deny = ["Bash(git push --force *)","Bash(sudo *)","Bash(rm -rf *)","Bash(curl *)","Bash(wget *)","Read(.env)","Read(.env.*)"] | .permissions.additionalDirectories = [] | .sandbox.enabled = true | .sandbox.filesystem.allowWrite = ["./"] | .sandbox.filesystem.denyRead = ["//etc","//usr","//home/*/.ssh","//root","~/.*"] )'.claude/settings.local.json>.claude/settings.local.json.tmp&&mv.claude/settings.local.json.tmp .claude/settings.local.json第一条是新建/完整覆盖,绝大多数场景用第一条就够。
mkdir -p .claude
-p参数核心行为:- 如果
.claude目录不存在 → 创建它 - 如果
.claude已经存在 → 不报错、不覆盖、不改动原有内容,直接静默成功
- 如果
mkdir本身永远不会覆盖文件/目录,-p只是防止“目录已存在”抛出错误。
对比
mkdir.claude# 目录已存在:报错 mkdir: cannot create directory ‘.claude’: File existsmkdir-p.claude# 目录已存在:啥也不干,返回0;不存在就新建真正会覆盖的是后面的> .claude/settings.local.json
jq-n'{...}'>.claude/settings.local.json>是输出重定向:如果文件已存在,直接清空覆盖原有内容;这才是覆盖点,不是mkdir‑p。
如果你不想覆盖旧配置,想合并更新,就要用我上一条给的 tmp 文件方式,不能直接
>。
完整拆解整条命令
mkdir-p.claude&&jq-n'{ ... }'>.claude/settings.local.jsonmkdir -p .claude:确保目录一定存在,存在就跳过;&&:前面成功才执行后面;jq -n:生成全新 json;> .claude/settings.local.json:覆盖写入该文件,旧内容全部丢失。
小坑提醒
.claude是目录;settings.local.json是目录里面的文件。- 万一你错误提前创建了一个名叫
.claude的普通文件(不是文件夹),mkdir -p .claude会报错,这是异常情况。
安全变体:不直接覆盖,先备份旧文件
mkdir-p.claude&&[-f.claude/settings.local.json]&&cp.claude/settings.local.json .claude/settings.local.json.bak&&jq-n'{...}'>.claude/settings.local.json检测旧配置存在就自动备份一份.bak,再写入新配置。