惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
S
Security Affairs
阮一峰的网络日志
阮一峰的网络日志
L
LangChain Blog
Microsoft Azure Blog
Microsoft Azure Blog
雷峰网
雷峰网
Recent Announcements
Recent Announcements
WordPress大学
WordPress大学
The GitHub Blog
The GitHub Blog
博客园_首页
The Cloudflare Blog
M
MIT News - Artificial intelligence
博客园 - 【当耐特】
MyScale Blog
MyScale Blog
S
SegmentFault 最新的问题
P
Proofpoint News Feed
Y
Y Combinator Blog
Jina AI
Jina AI
博客园 - 聂微东
A
About on SuperTechFans
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
G
Google Developers Blog
云风的 BLOG
云风的 BLOG
F
Full Disclosure
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Microsoft Security Blog
Microsoft Security Blog
爱范儿
爱范儿
T
Tailwind CSS Blog
J
Java Code Geeks
Vercel News
Vercel News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Stack Overflow Blog
Stack Overflow Blog
罗磊的独立博客
小众软件
小众软件
酷 壳 – CoolShell
酷 壳 – CoolShell
T
The Blog of Author Tim Ferriss
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
博客园 - 三生石上(FineUI控件)
W
WeLiveSecurity
PCI Perspectives
PCI Perspectives
Attack and Defense Labs
Attack and Defense Labs
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
宝玉的分享
宝玉的分享
IT之家
IT之家
Hacker News: Ask HN
Hacker News: Ask HN
The Register - Security
The Register - Security
T
The Exploit Database - CXSecurity.com
T
Threat Research - Cisco Blogs

暗无天日

读:AI Agent 安全日志——从可见性与隐私的两难说起 - 暗无天日 读:AI Agent 生产化——一份从原型到上线的速查清单 - 暗无天日 AI写作的语言指纹——如何让文字不那么像机器 - 暗无天日 读:50 条 Claude Code 技巧——一个工程经理的六个月使用心得 读:AI 辅助开发为什么让 E2E 测试更有价值 - 暗无天日 读:在Emacs中使用Claude Code(Spacemacs适配版) - 暗无天日 Claude Code 背后的工程哲学——读 Agent Harness Engineering 读:Agent Harness Engineering——AI 智能体不只是模型,还有套件 - 暗无天日 browser-harness:让 AI 直接接管你的浏览器 - 暗无天日 读:Security-First CI/CD —— DevSecOps 自动化实践指南 TIL: 数字小键盘的小数点陷阱与行内算术求值 - 暗无天日 读:Immutability 不是万能药,它是一种权衡 - 暗无天日 Conducty:给 Claude Code 加上项目记忆和并行执行能力 - 暗无天日 读 — GitHub Trending 里的 Claude Code 技能包 读 — Prompt Caching 省钱指南 TIL: Emacs 中那些跟鼠标配合的冷门快捷键 - 暗无天日 读:Anvil——把 Emacs 变成 AI 的工具服务器 读:Emacs 代码折叠终极指南 - 暗无天日 读:Clojure 搭车客指南 - 暗无天日 git推送失败后恢复仓库损坏的完整记录 - 暗无天日 多智能体系统的两个有效模式——以及对 Claude Code 用户的启示 - 暗无天日 用 Org Babel 写 Literate 博文:扩展执行 + 定制导出 proced:Emacs 内置的进程查看器 - 暗无天日 从 proced 定制中学到的 Elisp 模式 读:让 Emacs proced 在 macOS 上显示 CPU 和内存 异步编程的函数着色税 - 暗无天日 链式调用的代价:JavaScript 和 Clojure 的共同教训 - 暗无天日 hyperfine:命令行基准测试工具 - 暗无天日 管道中的变量去哪了?——子 shell 作用域陷阱 - 暗无天日 开源包装器的信任陷阱:四个危险信号 - 暗无天日 程序员愿意为 AI 写文档,却不愿为同事写 - 暗无天日 mktemp: Shell 脚本中临时文件的安全陷阱与最佳实践 - 暗无天日 WSL9x —— 在 Windows 9x 里跑 Linux 内核 6.19 用 ox.el 做你想做的事 —— org-export 高级编程指南 读:Hot-wiring the Lisp Machine —— 用纯 Elisp 构建零依赖的 Org 静态站点生成器 Elisp 性能优化的六个实战教训 - 暗无天日 fcitx5 下 Emacs 无法切换输入法的排查 - 暗无天日 ERT 测试交互命令的三种方式 - 暗无天日 SEM Assistant: 当 Elisp 守护进程遇上 LLM 用 dmsg 给 Elisp 加上结构化调试日志 用 org-habit 追踪非每日习惯 - 暗无天日 Clojure X-Men:当编程语言特性变成超能力 - 暗无天日 TIL: 用 diff-hl 在 fringe 中显示 git 变更 读:llm-test —— 用 LLM agent 驱动 Emacs 测试 TIL: AI 时代的橡皮鸭调试 - 暗无天日 fcitx 启动后键盘输入卡顿的排查 - 暗无天日 TIL: 早期网页的图片热区导航 - 暗无天日 读 Seeing the Whole System 用 Emacs 自动生成每周链接推荐 - 暗无天日 读:ASCII control characters in my terminal 读 What to learn - 暗无天日 Lisp 的括号之痛——一个愚人节玩笑揭开的老伤疤 - 暗无天日 一本书该"线性读"还是"并行读" - 暗无天日 读 How to Monetize a Blog:一篇伪装成变现指南的讽刺文 Python Mock 第三方依赖的四种策略 - 暗无天日 Emacs Lisp 热重载实用指南 - 暗无天日 Prot 的 Emacs 配置哲学 - 暗无天日 TIL: 从直播对谈中学到的三个 Emacs 技巧 - 暗无天日 TIL: 自动使用项目虚拟环境的 Python - 暗无天日 TIL: 让 Help buffer 自动获得焦点 一条命令让本地开发用上 HTTPS —— slim 工具介绍 用 fsck 检查和修复 Linux 文件系统 排查Linux进程"卡死"实战:从strace到gdb全流程 - 暗无天日 PostgreSQL 索引:从基础到你可能不知道的高级用法 - 暗无天日 用 .pdbrc 自定义 Python 调试器 ANSI 转义码的标准化现状 - 暗无天日 终端程序的潜规则 - 暗无天日 PARA Org-mode 测试配置 - 暗无天日 AI越强越辣鸡?控制论说这是必然的 - 暗无天日 AI 越强越需要你盯着——反馈循环实操指南 - 暗无天日 你的AI代理正在偷你的密钥——四种你没想到的泄露通道 - 暗无天日 LLM 在 DevOps 中的三种角色 - 暗无天日 写作风格的反建议 - 暗无天日 反驳本质复杂性——Dan Luu 论为什么《没有银弹》错了 - 暗无天日 文件充满了危险——Dan Luu 谈文件系统的可靠性陷阱 - 暗无天日 AI 时代的 PARA 方法:用 Org-mode 和 AI 打造个人知识管理系统 Linux 数据去重学习笔记 - 暗无天日 创建跨平台 ZIP 文件的隐藏陷阱:Extra Field - 暗无天日 X11 Forwarding 排障指南 - 暗无天日 IP欺骗端口扫描:当别人冒充你去扫描别人 - 暗无天日 Linux 输入栈全景解析:从硬件按键到屏幕响应 - 暗无天日 Unix 系统中那些被埋没的配置开关——以 FontConfig 为例 - 暗无天日 在Linux上限制儿童使用电脑 - 暗无天日 GIF不仅仅是一种图片格式——用GIF流做些奇怪的事 - 暗无天日 Leiningen 学习笔记:Clojure 项目构建与管理从入门到实战配置 - 暗无天日 Google SRE Book 读书笔记 - 暗无天日 yes 管道 head 发生了什么 - 暗无天日 为什么 nohup 在 crontab 中不起作用 Linux PAM 简介 - 暗无天日 从Linux ISO文件启动计算机 - 暗无天日 用 Bash 打造一个Screen Locker 用GitHub Actions自动构建EGO博客 - 暗无天日 blocking I/O 的作用 - 暗无天日 mobileog 手机端同步提示Error:2 No such file 的解决方法 回收 WSL2 VHDX 文件占用空间 使用 org-mode columnview 生成任务列表 - 暗无天日 Emacs 作为 MPD 客户端 - 暗无天日 移动文件路径却不破坏org file link的方法 - 暗无天日 如何合理的导出help link 成HTML - 暗无天日 笑话理解之Biology - 暗无天日
Bash中的Indirection与Nameref - 暗无天日
2026-04-11 · via 暗无天日

在 Bash 中,有时候你需要通过一个变量来间接引用另一个变量。比如你知道变量名是一个字符串,你想根据这个字符串去访问对应的变量值。Bash 提供了两种机制来实现这个需求: Indirection (间接展开)和 Nameref (名称引用)。

Indirection:间接展开

基本用法

Indirection 的语法很简单:在参数展开中,在变量名前面加一个 ! 就行了。

fruit="apple"
var="fruit"

echo "${!var}"
apple
apple

这里 var 的值是 "fruit" ,而 ${!var} 会把 var 的值(也就是 "fruit" )当作变量名,再去取那个变量的值,最终得到 "apple" 。这就是"间接"的含义。

注意, ${!var} 只做 一层 间接,不会递归展开。即使 apple 也是一个变量名, ${!var} 也不会再去取 apple 的值。

一个实际例子:遍历配置变量

假设你有好几个配置项,想根据名字动态地读取它们的值:

db_host="192.168.1.100"
db_port="3306"
db_user="admin"

for item in host port user; do
    varname="db_$item"
    echo "$item = ${!varname}"
done
host = 192.168.1.100
port = 3306
user = admin
host = 192.168.1.100
port = 3306
user = admin

通过拼接变量名再使用 ${!varname} 间接展开,可以避免写一堆 if/elif 或者 case 分支。

注意 ! 的歧义

! 在参数展开中有两种不同的含义,容易混淆:

  • ${!name} — 间接展开,取 name 的值所指向的变量
  • ${!name[@]}${!name[*]} — 展开数组 name 的所有 索引 (下标)
fruit="apple"
name="fruit"

echo "${!name}"     
apple
arr=("a" "b" "c")
name="arr"

echo "${!name}"     
a

注意这里的结果是 a 而不是 a b c 。因为对数组变量做间接展开时,等同于 ${arr} ,而 ${arr} 实际上就是 ${arr[0]} ,只展开数组的第一个元素。如果想通过间接方式获取数组的所有元素,Nameref 是更好的选择。

arr=("a" "b" "c")
echo "${!arr[@]}"   
0 1 2
0 1 2

简单记:有 [@][*] 时, ! 取的是索引;没有时, ! 是间接展开。

Indirection 的局限

Indirection 最大的局限是它 只能读,不能写 。你不能通过 ${!var} 来给目标变量赋值:

fruit="apple"
var="fruit"

${!var}="banana"   
bash: apple=banana: command not found...

如果你需要通过间接方式 修改 一个变量,就需要用到下面介绍的 Nameref 了。

Nameref:名称引用

基本用法

Nameref 从 Bash 4.3 开始支持,通过 declare -nlocal -n 声明一个"引用变量":

fruit="apple"
declare -n ref=fruit

echo "$ref"       
apple
ref="banana"      echo "$fruit"
banana

ref 不是 fruit 的副本,而是指向 fruit 的一个"别名"。对 ref 的任何读写操作,实际上都作用于 fruit

一个有趣的细节: ${!nameref} 行为不同

当你对 Nameref 变量使用 ${!ref} 时,它 不是 间接展开,而是返回被引用变量的 名字

var="hello"
declare -n ref=var

echo "$ref"        echo "${!ref}"     
hello
var

这个特性在调试时很有用,可以让你知道一个 Nameref 到底指向了哪个变量。在函数中结合 ${FUNCNAME[0]} 可以实现很好的日志输出:

update_value() {
    local -n up_ref=$1
    echo "函数 ${FUNCNAME[0]} 正在修改变量 '${!up_ref}'"
    up_ref="new_value"
}

my_var="old_value"
update_value my_var
echo "调用后 my_var = $my_var"
函数 update_value 正在修改变量 'my_var'
调用后 my_var = new_value

在函数中使用 Nameref

这是 Nameref 最常见的使用场景。当你需要让函数修改调用者的变量时,Nameref 比传统的 eval 或 Indirection 要安全得多。

swap() {
    local -n a=$1
    local -n b=$2
    local tmp="$a"
    a="$b"
    b="$tmp"
}

x=10
y=20
swap x y
echo "x=$x, y=$y"
x=20, y=10

注意函数里用 local -n 而不是 declare -n ,这样引用变量的作用域就限制在函数内部,不会污染全局命名空间。

操作关联数组

Nameref 在操作关联数组(associative array)时尤其好用:

update_config() {
    local -n config=$1
    config[host]="newhost.example.com"
    config[port]="8443"
    config[enabled]="yes"
}

declare -A server_config
update_config server_config

echo "${server_config[host]}"
echo "${server_config[port]}"
echo "${server_config[enabled]}"
newhost.example.com
8443
yes

如果用 Indirection,你没法直接操作关联数组的元素——因为 ${!var} 只能取出整个变量的值,不能写成 ${!var[key]} 这种形式。而 Nameref 完美解决了这个问题。

小心 Nameref 循环

两个 Nameref 变量互相引用会造成循环:

declare -n a=b
declare -n b=a
echo "$a"   

Bash 会检测到循环并报错,不会真的无限递归,但你仍然要避免写出这样的代码。

Indirection vs Nameref 对比

特性 Indirection (${!var}) Nameref (declare -n)
最低 Bash 版本 2.0+ 4.3+
能否读取
能否写入 不能
生命周期 一次性展开 持久引用
操作数组元素 不方便 方便
在函数中使用 需配合 eval 才能写入 直接使用,更安全

什么时候用哪个?

  • 只需要 读取 另一个变量的值,或者需要兼容老版本 Bash → 用 Indirection
  • 需要 修改 另一个变量的值,或者在函数中传递复杂数据结构 → 用 Nameref
  • 能用 Nameref 的场景,优先用 Nameref——它语义更清晰,也更安全

参考