目标:仅当前工作目录读写,不允许碰系统/外部目录;普通用户运行,不用 sudo,避开 --dangerously‑skip‑permissions root 报错
核心:不要 sudo ,进入你的项目目录,创建项目本地配置
.claude/settings.local.json(加入.gitignore,不提交版本库)
1、在你的项目目录创建配置
bash
mkdir -p .claude
vim .claude/settings.local.json
写入下面配置:
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,保持普通用户账号执行:
bash
# 正确:进入项目目录,普通用户启动
cd /your/project
claude
2、会话内快速切换(不用改配置文件)
进入 claude 交互终端后输入:
/permissions
菜单选择:acceptEdits(接受编辑),效果:本会话内当前目录读写自动放行,shell命令仍询问。
3、如果你需要非交互脚本运行(无终端交互)
不能用 --dangerously‑skip‑permissions,改用 --permission-mode acceptEdits
bash
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 通配,仅允许安全命令,禁止危险命令:
json
"allow": ["Read","Write","Glob","Grep","Bash(git *)","Bash(npm *)","Bash(pytest *)"]
排错
- 如果 claude 还试图访问外面文件:确认运行时 CWD 是你的项目目录,不要在
$HOME根目录运行。 - 检查配置是否生效:
bash
claude debug settings
会打印合并后的完整权限配置,确认 sandbox、permissions 读取的是你项目下 .claude/settings.local.json。
完整配置(当前目录读写 + Git权限)
.claude/settings.local.json,务必加入 .gitignore,普通用户运行 claude,不要 sudo。
两种方案:
方案A(推荐,安全优先)
常用git操作自动放行;push / merge / push‑force仍然弹窗确认,防止误推送代码到远程仓库。
json
{
"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,不会弹窗确认。
json
{
"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规则被加载。
命令行临时一次性会话(不写配置文件)
bash
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 的完整命令行
bash
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)
bash
alias claude-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 之后直接:
bash
claude-proj
最佳实践建议
- 权限工具白名单(allow/ask/deny):用命令行/别名,方便临时切换;
- 目录沙盒隔离(sandbox.filesystem):保留项目目录下
.claude/settings.local.json,命令行做不到。
调试查看最终生效工具列表:
bash
claude debug tools
如果你要做非交互一次性任务(-p 传入prompt):
bash
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
bash
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
bash
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
执行完直接落地文件,无需手动编辑。
校验是否写对
bash
cat .claude/settings.local.json
如果想追加到 .gitignore(一并一行)
bash
echo ".claude/settings.local.json" >> .gitignore
如果你是想修改已有json,而不是覆盖
覆盖是上面;如果已有 settings.local.json,只想更新权限字段,用这个(不会清空其他已有key)
bash
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只是防止"目录已存在"抛出错误。
对比
bash
mkdir .claude # 目录已存在:报错 mkdir: cannot create directory '.claude': File exists
mkdir -p .claude # 目录已存在:啥也不干,返回0;不存在就新建
真正会覆盖的是后面的 > .claude/settings.local.json
bash
jq -n '{...}' > .claude/settings.local.json
> 是输出重定向:如果文件已存在,直接清空覆盖原有内容 ;这才是覆盖点,不是 mkdir‑p。
如果你不想覆盖旧配置,想合并更新,就要用我上一条给的 tmp 文件方式,不能直接
>。
完整拆解整条命令
bash
mkdir -p .claude && jq -n '{ ... }' > .claude/settings.local.json
mkdir -p .claude:确保目录一定存在,存在就跳过;&&:前面成功才执行后面;jq -n:生成全新 json;> .claude/settings.local.json:覆盖写入该文件,旧内容全部丢失。
小坑提醒
.claude是目录;settings.local.json是目录里面的文件。- 万一你错误提前创建了一个名叫
.claude的普通文件 (不是文件夹),mkdir -p .claude会报错,这是异常情况。
安全变体:不直接覆盖,先备份旧文件
bash
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,再写入新配置。