一、什么是位置参数
位置参数(Positional Parameters)是 Shell 脚本接收命令行输入的基础机制。当你执行 ./script.sh arg1 arg2 arg3 时,Shell 会自动将这些参数映射到一组特殊变量中:
| 变量 | 含义 | 示例值 |
|---|---|---|
$0 |
当前脚本名称 | ./script.sh |
$1 ~ $9 |
第 1~9 个参数 | arg1, arg2 |
${10} |
第 10 个及以后的参数 | arg10 |
$# |
参数个数(不含 $0) |
3 |
$* |
所有参数,作为一个字符串 | "arg1 arg2 arg3" |
$@ |
所有参数,作为独立字符串数组 | "arg1" "arg2" "arg3" |
$? |
上条命令退出状态码 | 0(成功) |
$$ |
当前 Shell 进程 PID | 12345 |
记忆技巧 :
$#的#像是在数数(count),$?的?像是在问"结果如何",$$两个$表示"我自己的身份"。
1.1 基础用法
bash
#!/bin/bash
echo "脚本名称: $0"
echo "第一个参数: $1"
echo "第二个参数: $2"
echo "参数总数: $#"
执行:
bash
$ ./demo.sh hello world
echo "脚本名称: ./demo.sh"
echo "第一个参数: hello"
echo "第二个参数: world"
echo "参数总数: 2"
1.2 超过 9 个参数的陷阱
当参数超过 9 个时,必须用花括号:
bash
#!/bin/bash
echo "第10个参数: ${10}" # ✅ 正确
echo "第10个参数: $10" # ❌ 错误!会被解析为 $1 + "0"
执行 ./test.sh a b c d e f g h i j:
${10}→j$10→a0($1的值a后面拼接字符0)
二、\* 与 @ 的区别:Shell 面试的"送命题"
这是 Shell 脚本中最经典、也最容易让人困惑的知识点。两者的区别只在加引号时体现。
2.1 不加引号时:行为完全相同
bash
#!/bin/bash
echo "=== 不加引号 ==="
echo "使用 \$*:"
for arg in $*; do
echo " - [$arg]"
done
echo "使用 \$@:"
for arg in $@; do
echo " - [$arg]"
done
执行 ./test.sh "hello world" foo bar:
=== 不加引号 ===
使用 $*:
- [hello]
- [world]
- [foo]
- [bar]
使用 $@:
- [hello]
- [world]
- [foo]
- [bar]
注意 :包含空格的参数 "hello world" 被拆分成两个词!这是因为不加引号时,Shell 会对展开结果进行单词拆分(Word Splitting)。
2.2 加引号时:天壤之别
bash
#!/bin/bash
echo "=== 加引号 ==="
echo "使用 \"\$*\":"
for arg in "$*"; do
echo " -> [$arg]"
done
echo "使用 \"\$@\":"
for arg in "$@"; do
echo " -> [$arg]"
done
执行 ./test.sh "hello world" foo bar:
=== 加引号 ===
使用 "$*":
-> [hello world foo bar] ← 一个字符串
使用 "$@":
-> [hello world] ← 独立参数
-> [foo]
-> [bar]
2.3 区别总结
| 形式 | 展开结果 | 说明 |
|---|---|---|
$* |
hello world foo bar |
所有参数拆分为单词 |
$@ |
hello world foo bar |
所有参数拆分为单词 |
"$*" |
"hello world foo bar" |
一个字符串,用 IFS 第一个字符连接 |
"$@" |
"hello world" "foo" "bar" |
多个独立字符串,保留原始引号信息 |
最佳实践 :几乎所有情况下都应该使用
"$@",它能正确保留参数中的空格和特殊字符。
三、让人费解的案例
现在进入本文的核心------那些让人"怀疑人生"的引号与位置参数组合案例。
3.1 案例一:单引号下的 $@
bash
#!/bin/bash
echo '不加引号: '
echo $@
echo '单引号: '
echo '$@'
echo '双引号: '
echo "$@"
执行 ./test.sh "hello world" foo:
不加引号:
hello world foo
单引号:
$@ ← 字面量!
双引号:
hello world foo
原理 :单引号(')是"强引用",会剥夺其中所有字符 的特殊含义。$@ 在单引号里不会被展开,原样输出为字面字符串 $@。双引号(")是"弱引用",允许 $ 进行参数展开。
3.2 案例二:echo '$@' | xargs 的陷阱
这是最常见的坑之一:
bash
#!/bin/bash
# 假设我们想将所有参数通过管道传给另一个命令
# 错误写法 1:
echo '$@' | xargs echo
# 输出: $@ ← 只传了字面量 "$@"
# 错误写法 2:
echo $@ | xargs echo
# 输出: hello world foo bar ← 空格被拆分了,参数边界丢失
# 错误写法 3:
echo "$@" | xargs echo
# 输出: hello world foo bar ← 所有参数合并成一个字符串
# 正确写法:
printf '%s\n' "$@" | xargs -I {} echo "处理: {}"
# 或更简单地直接传参
some_command "$@"
问题分析:
echo '$@'→ 单引号阻止展开,管道接收到的就是两个字符$和@echo $@→ 先展开为hello world foo bar,然后echo输出这个字符串,管道接收的是合并后的一个字符串echo "$@"→ 展开为hello world foo bar(一个字符串),同样丢失了参数边界printf '%s\n' "$@"→"$@"展开为多个独立参数,printf每个参数单独输出一行,管道正确传递
3.3 案例三:管道中的子 Shell 陷阱
bash
#!/bin/bash
var="before"
echo "$var" | while read line; do
var="after"
echo "循环内: $var"
done
echo "循环外: $var"
输出:
before
循环内: after
循环外: before ← 没变!
原理 :管道符 | 会创建子 Shell(Subshell),子 Shell 中对变量的修改不会影响父 Shell。这是 Shell 中另一个让人困惑的地方,与位置参数传递密切相关。
3.4 案例四:IFS 与 "$*" 的隐藏行为
bash
#!/bin/bash
# 默认 IFS 是 "\t\n"(空格、制表符、换行)
echo "默认 IFS:"
echo "$*"
# 修改 IFS
IFS=","
echo "修改 IFS 为逗号:"
echo "$*"
# 再改回来
IFS=" "
echo "改回空格:"
echo "$*"
执行 ./test.sh a b c:
默认 IFS:
a b c
修改 IFS 为逗号:
a,b,c
改回空格:
a b c
关键点 :"$*" 使用 IFS 的第一个字符作为连接符,而 "$@" 不受 IFS 影响,始终保留独立参数。
3.5 案例五:嵌套引号的"俄罗斯套娃"
bash
#!/bin/bash
name="Alice"
# 想要输出: Alice said "hello"
echo "$name said \"hello\"" # ✅ 正确
echo '$name said "hello"' # ❌ 输出: $name said "hello"
echo "$name said 'hello'" # ✅ 正确
echo "$name said \'hello\'" # ✅ 正确
# 更复杂的嵌套
cmd="echo 'hello world'"
echo "$cmd" # 输出: echo 'hello world'
eval "$cmd" # 执行: hello world
引号规则速查:
- 单引号
':内部所有字符都是字面量,不允许嵌套单引号 - 双引号
":内部$、`````、"、\保持特殊含义 - 单引号内不能放单引号,双引号内可以放单引号
3.6 案例六:shift 命令与位置参数
bash
#!/bin/bash
echo "原始参数:"
echo " \$1=$1, \$2=$2, \$3=$3, 总数=\$#=$#"
shift
echo "shift 一次后:"
echo " \$1=$1, \$2=$2, \$3=$3, 总数=\$#=$#"
shift 2
echo "shift 两次后:"
echo " \$1=$1, \$2=$2, 总数=\$#=$#"
执行 ./test.sh apple banana cherry date:
原始参数:
$1=apple, $2=banana, $3=cherry, 总数=$#=4
shift 一次后:
$1=banana, $2=cherry, $3=date, 总数=$#=3
shift 两次后:
$1=date, $2=, 总数=$#=1
用途 :shift 常用于遍历所有参数,配合 while [ $# -gt 0 ] 实现命令行选项解析。
3.7 案例七:set 命令重置位置参数
bash
#!/bin/bash
echo "原始参数: $@"
# 用 set 重置位置参数
set -- "new arg" "another one" "third"
echo "重置后参数: $@"
echo "\$1=$1, \$2=$2, \$3=$3"
输出:
原始参数: old1 old2
重置后参数: new arg another one third
$1=new arg, $2=another one, $3=third
注意 :set -- 中的 -- 是必需的,防止后续参数以 - 开头时被误解析为 set 的选项。
四、位置参数传递流程
4.1 Shell 参数解析与传递全景图
#mermaid-svg-U3OzDLz8liFzO9z4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-U3OzDLz8liFzO9z4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-U3OzDLz8liFzO9z4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-U3OzDLz8liFzO9z4 .error-icon{fill:#552222;}#mermaid-svg-U3OzDLz8liFzO9z4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-U3OzDLz8liFzO9z4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-U3OzDLz8liFzO9z4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-U3OzDLz8liFzO9z4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-U3OzDLz8liFzO9z4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-U3OzDLz8liFzO9z4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-U3OzDLz8liFzO9z4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-U3OzDLz8liFzO9z4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-U3OzDLz8liFzO9z4 .marker.cross{stroke:#333333;}#mermaid-svg-U3OzDLz8liFzO9z4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-U3OzDLz8liFzO9z4 p{margin:0;}#mermaid-svg-U3OzDLz8liFzO9z4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-U3OzDLz8liFzO9z4 .cluster-label text{fill:#333;}#mermaid-svg-U3OzDLz8liFzO9z4 .cluster-label span{color:#333;}#mermaid-svg-U3OzDLz8liFzO9z4 .cluster-label span p{background-color:transparent;}#mermaid-svg-U3OzDLz8liFzO9z4 .label text,#mermaid-svg-U3OzDLz8liFzO9z4 span{fill:#333;color:#333;}#mermaid-svg-U3OzDLz8liFzO9z4 .node rect,#mermaid-svg-U3OzDLz8liFzO9z4 .node circle,#mermaid-svg-U3OzDLz8liFzO9z4 .node ellipse,#mermaid-svg-U3OzDLz8liFzO9z4 .node polygon,#mermaid-svg-U3OzDLz8liFzO9z4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-U3OzDLz8liFzO9z4 .rough-node .label text,#mermaid-svg-U3OzDLz8liFzO9z4 .node .label text,#mermaid-svg-U3OzDLz8liFzO9z4 .image-shape .label,#mermaid-svg-U3OzDLz8liFzO9z4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-U3OzDLz8liFzO9z4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-U3OzDLz8liFzO9z4 .rough-node .label,#mermaid-svg-U3OzDLz8liFzO9z4 .node .label,#mermaid-svg-U3OzDLz8liFzO9z4 .image-shape .label,#mermaid-svg-U3OzDLz8liFzO9z4 .icon-shape .label{text-align:center;}#mermaid-svg-U3OzDLz8liFzO9z4 .node.clickable{cursor:pointer;}#mermaid-svg-U3OzDLz8liFzO9z4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-U3OzDLz8liFzO9z4 .arrowheadPath{fill:#333333;}#mermaid-svg-U3OzDLz8liFzO9z4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-U3OzDLz8liFzO9z4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-U3OzDLz8liFzO9z4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-U3OzDLz8liFzO9z4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-U3OzDLz8liFzO9z4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-U3OzDLz8liFzO9z4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-U3OzDLz8liFzO9z4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-U3OzDLz8liFzO9z4 .cluster text{fill:#333;}#mermaid-svg-U3OzDLz8liFzO9z4 .cluster span{color:#333;}#mermaid-svg-U3OzDLz8liFzO9z4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-U3OzDLz8liFzO9z4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-U3OzDLz8liFzO9z4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-U3OzDLz8liFzO9z4 .icon-shape,#mermaid-svg-U3OzDLz8liFzO9z4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-U3OzDLz8liFzO9z4 .icon-shape p,#mermaid-svg-U3OzDLz8liFzO9z4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-U3OzDLz8liFzO9z4 .icon-shape .label rect,#mermaid-svg-U3OzDLz8liFzO9z4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-U3OzDLz8liFzO9z4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-U3OzDLz8liFzO9z4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-U3OzDLz8liFzO9z4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 美元星号 或 美元at
双引号加美元星号
双引号加美元at
命令行输入: 点斜杠script.sh 双引号hello world双引号 foo bar
Shell 词法分析
引号配对与识别
参数拆分
位置0 = 点斜杠script.sh
位置1 = hello world
位置2 = foo
位置3 = bar
参数个数 = 3
脚本执行
引用方式?
空格切分
hello/world/foo/bar
单个字符串
hello world foo bar
独立字符串数组
双引号hello world双引号 双引号foo双引号 双引号bar双引号
传递给命令
4.2 引号展开顺序与影响
#mermaid-svg-TnFW7ePRQGQApHYx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TnFW7ePRQGQApHYx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TnFW7ePRQGQApHYx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TnFW7ePRQGQApHYx .error-icon{fill:#552222;}#mermaid-svg-TnFW7ePRQGQApHYx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TnFW7ePRQGQApHYx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TnFW7ePRQGQApHYx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TnFW7ePRQGQApHYx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TnFW7ePRQGQApHYx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TnFW7ePRQGQApHYx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TnFW7ePRQGQApHYx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TnFW7ePRQGQApHYx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TnFW7ePRQGQApHYx .marker.cross{stroke:#333333;}#mermaid-svg-TnFW7ePRQGQApHYx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TnFW7ePRQGQApHYx p{margin:0;}#mermaid-svg-TnFW7ePRQGQApHYx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TnFW7ePRQGQApHYx .cluster-label text{fill:#333;}#mermaid-svg-TnFW7ePRQGQApHYx .cluster-label span{color:#333;}#mermaid-svg-TnFW7ePRQGQApHYx .cluster-label span p{background-color:transparent;}#mermaid-svg-TnFW7ePRQGQApHYx .label text,#mermaid-svg-TnFW7ePRQGQApHYx span{fill:#333;color:#333;}#mermaid-svg-TnFW7ePRQGQApHYx .node rect,#mermaid-svg-TnFW7ePRQGQApHYx .node circle,#mermaid-svg-TnFW7ePRQGQApHYx .node ellipse,#mermaid-svg-TnFW7ePRQGQApHYx .node polygon,#mermaid-svg-TnFW7ePRQGQApHYx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TnFW7ePRQGQApHYx .rough-node .label text,#mermaid-svg-TnFW7ePRQGQApHYx .node .label text,#mermaid-svg-TnFW7ePRQGQApHYx .image-shape .label,#mermaid-svg-TnFW7ePRQGQApHYx .icon-shape .label{text-anchor:middle;}#mermaid-svg-TnFW7ePRQGQApHYx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TnFW7ePRQGQApHYx .rough-node .label,#mermaid-svg-TnFW7ePRQGQApHYx .node .label,#mermaid-svg-TnFW7ePRQGQApHYx .image-shape .label,#mermaid-svg-TnFW7ePRQGQApHYx .icon-shape .label{text-align:center;}#mermaid-svg-TnFW7ePRQGQApHYx .node.clickable{cursor:pointer;}#mermaid-svg-TnFW7ePRQGQApHYx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TnFW7ePRQGQApHYx .arrowheadPath{fill:#333333;}#mermaid-svg-TnFW7ePRQGQApHYx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TnFW7ePRQGQApHYx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TnFW7ePRQGQApHYx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TnFW7ePRQGQApHYx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TnFW7ePRQGQApHYx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TnFW7ePRQGQApHYx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TnFW7ePRQGQApHYx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TnFW7ePRQGQApHYx .cluster text{fill:#333;}#mermaid-svg-TnFW7ePRQGQApHYx .cluster span{color:#333;}#mermaid-svg-TnFW7ePRQGQApHYx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TnFW7ePRQGQApHYx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TnFW7ePRQGQApHYx rect.text{fill:none;stroke-width:0;}#mermaid-svg-TnFW7ePRQGQApHYx .icon-shape,#mermaid-svg-TnFW7ePRQGQApHYx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TnFW7ePRQGQApHYx .icon-shape p,#mermaid-svg-TnFW7ePRQGQApHYx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TnFW7ePRQGQApHYx .icon-shape .label rect,#mermaid-svg-TnFW7ePRQGQApHYx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TnFW7ePRQGQApHYx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TnFW7ePRQGQApHYx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TnFW7ePRQGQApHYx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 单引号
双引号
无引号
单引号在内
双引号在内
原始命令
外层引号类型
强引用: 所有字符字面量
美元at → 字面量 美元at
弱引用: 允许美元展开
美元at → 参数展开
默认处理: 空格切分
美元at → 拆分后传递
内层引号?
单引号被双引号包裹 → 字面量
双引号内容 单引号 hi 单引号
需转义: 反斜杠双引号
双引号内容 反斜杠 hi 反斜杠
最终传递给命令
4.3 管道与子 Shell 关系图
#mermaid-svg-UgrQZzh7seZl66iQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UgrQZzh7seZl66iQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UgrQZzh7seZl66iQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UgrQZzh7seZl66iQ .error-icon{fill:#552222;}#mermaid-svg-UgrQZzh7seZl66iQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UgrQZzh7seZl66iQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UgrQZzh7seZl66iQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UgrQZzh7seZl66iQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UgrQZzh7seZl66iQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UgrQZzh7seZl66iQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UgrQZzh7seZl66iQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UgrQZzh7seZl66iQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UgrQZzh7seZl66iQ .marker.cross{stroke:#333333;}#mermaid-svg-UgrQZzh7seZl66iQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UgrQZzh7seZl66iQ p{margin:0;}#mermaid-svg-UgrQZzh7seZl66iQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-UgrQZzh7seZl66iQ .cluster-label text{fill:#333;}#mermaid-svg-UgrQZzh7seZl66iQ .cluster-label span{color:#333;}#mermaid-svg-UgrQZzh7seZl66iQ .cluster-label span p{background-color:transparent;}#mermaid-svg-UgrQZzh7seZl66iQ .label text,#mermaid-svg-UgrQZzh7seZl66iQ span{fill:#333;color:#333;}#mermaid-svg-UgrQZzh7seZl66iQ .node rect,#mermaid-svg-UgrQZzh7seZl66iQ .node circle,#mermaid-svg-UgrQZzh7seZl66iQ .node ellipse,#mermaid-svg-UgrQZzh7seZl66iQ .node polygon,#mermaid-svg-UgrQZzh7seZl66iQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UgrQZzh7seZl66iQ .rough-node .label text,#mermaid-svg-UgrQZzh7seZl66iQ .node .label text,#mermaid-svg-UgrQZzh7seZl66iQ .image-shape .label,#mermaid-svg-UgrQZzh7seZl66iQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-UgrQZzh7seZl66iQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UgrQZzh7seZl66iQ .rough-node .label,#mermaid-svg-UgrQZzh7seZl66iQ .node .label,#mermaid-svg-UgrQZzh7seZl66iQ .image-shape .label,#mermaid-svg-UgrQZzh7seZl66iQ .icon-shape .label{text-align:center;}#mermaid-svg-UgrQZzh7seZl66iQ .node.clickable{cursor:pointer;}#mermaid-svg-UgrQZzh7seZl66iQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UgrQZzh7seZl66iQ .arrowheadPath{fill:#333333;}#mermaid-svg-UgrQZzh7seZl66iQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UgrQZzh7seZl66iQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UgrQZzh7seZl66iQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UgrQZzh7seZl66iQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UgrQZzh7seZl66iQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UgrQZzh7seZl66iQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UgrQZzh7seZl66iQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UgrQZzh7seZl66iQ .cluster text{fill:#333;}#mermaid-svg-UgrQZzh7seZl66iQ .cluster span{color:#333;}#mermaid-svg-UgrQZzh7seZl66iQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-UgrQZzh7seZl66iQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UgrQZzh7seZl66iQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-UgrQZzh7seZl66iQ .icon-shape,#mermaid-svg-UgrQZzh7seZl66iQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UgrQZzh7seZl66iQ .icon-shape p,#mermaid-svg-UgrQZzh7seZl66iQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UgrQZzh7seZl66iQ .icon-shape .label rect,#mermaid-svg-UgrQZzh7seZl66iQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UgrQZzh7seZl66iQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UgrQZzh7seZl66iQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UgrQZzh7seZl66iQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 子 Shell 进程 (管道创建)
父 Shell 进程
管道 |
var 仍为 before
变量 var=before
echo \@\\
位置参数: 1, 2, 3
while read line
var=after
修改仅在子Shell生效
父Shell中 var 不变
4.4 位置参数处理决策树
#mermaid-svg-sRlnwqayF0RRjKu9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sRlnwqayF0RRjKu9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sRlnwqayF0RRjKu9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sRlnwqayF0RRjKu9 .error-icon{fill:#552222;}#mermaid-svg-sRlnwqayF0RRjKu9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sRlnwqayF0RRjKu9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sRlnwqayF0RRjKu9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sRlnwqayF0RRjKu9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sRlnwqayF0RRjKu9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sRlnwqayF0RRjKu9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sRlnwqayF0RRjKu9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sRlnwqayF0RRjKu9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sRlnwqayF0RRjKu9 .marker.cross{stroke:#333333;}#mermaid-svg-sRlnwqayF0RRjKu9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sRlnwqayF0RRjKu9 p{margin:0;}#mermaid-svg-sRlnwqayF0RRjKu9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-sRlnwqayF0RRjKu9 .cluster-label text{fill:#333;}#mermaid-svg-sRlnwqayF0RRjKu9 .cluster-label span{color:#333;}#mermaid-svg-sRlnwqayF0RRjKu9 .cluster-label span p{background-color:transparent;}#mermaid-svg-sRlnwqayF0RRjKu9 .label text,#mermaid-svg-sRlnwqayF0RRjKu9 span{fill:#333;color:#333;}#mermaid-svg-sRlnwqayF0RRjKu9 .node rect,#mermaid-svg-sRlnwqayF0RRjKu9 .node circle,#mermaid-svg-sRlnwqayF0RRjKu9 .node ellipse,#mermaid-svg-sRlnwqayF0RRjKu9 .node polygon,#mermaid-svg-sRlnwqayF0RRjKu9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sRlnwqayF0RRjKu9 .rough-node .label text,#mermaid-svg-sRlnwqayF0RRjKu9 .node .label text,#mermaid-svg-sRlnwqayF0RRjKu9 .image-shape .label,#mermaid-svg-sRlnwqayF0RRjKu9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-sRlnwqayF0RRjKu9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sRlnwqayF0RRjKu9 .rough-node .label,#mermaid-svg-sRlnwqayF0RRjKu9 .node .label,#mermaid-svg-sRlnwqayF0RRjKu9 .image-shape .label,#mermaid-svg-sRlnwqayF0RRjKu9 .icon-shape .label{text-align:center;}#mermaid-svg-sRlnwqayF0RRjKu9 .node.clickable{cursor:pointer;}#mermaid-svg-sRlnwqayF0RRjKu9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sRlnwqayF0RRjKu9 .arrowheadPath{fill:#333333;}#mermaid-svg-sRlnwqayF0RRjKu9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sRlnwqayF0RRjKu9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sRlnwqayF0RRjKu9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sRlnwqayF0RRjKu9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sRlnwqayF0RRjKu9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sRlnwqayF0RRjKu9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sRlnwqayF0RRjKu9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sRlnwqayF0RRjKu9 .cluster text{fill:#333;}#mermaid-svg-sRlnwqayF0RRjKu9 .cluster span{color:#333;}#mermaid-svg-sRlnwqayF0RRjKu9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sRlnwqayF0RRjKu9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sRlnwqayF0RRjKu9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-sRlnwqayF0RRjKu9 .icon-shape,#mermaid-svg-sRlnwqayF0RRjKu9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sRlnwqayF0RRjKu9 .icon-shape p,#mermaid-svg-sRlnwqayF0RRjKu9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sRlnwqayF0RRjKu9 .icon-shape .label rect,#mermaid-svg-sRlnwqayF0RRjKu9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sRlnwqayF0RRjKu9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sRlnwqayF0RRjKu9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sRlnwqayF0RRjKu9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
是
否
需要传递所有参数?
参数中可能包含空格?
使用 &"@\&"
需要合并为一个字符串?
使用 \&"*&"
使用 @ 或 * 均可
通过管道?
用 printf '%s
' &"@\&" \| cmd
直接 cmd \&"@&"
注意 IFS 影响连接符
注意 Word Splitting
五、最佳实践与避坑指南
5.1 黄金法则
-
始终使用
"$@"传递参数bash# ✅ 正确 some_command "$@" # ❌ 错误 some_command $@ some_command $* -
变量引用始终加双引号
bash# ✅ 正确 echo "$filename" # ❌ 错误 echo $filename -
通过管道传递多参数时用
printfbash# ✅ 正确 printf '%s\n' "$@" | xargs -I {} process {} # ❌ 错误 echo "$@" | xargs process -
使用
shift遍历参数bashwhile [ $# -gt 0 ]; do echo "处理: $1" shift done -
设置严格模式
bash#!/usr/bin/env bash set -euo pipefail
5.2 常见错误速查表
| 错误写法 | 问题 | 正确写法 |
|---|---|---|
| `echo '$@' | cmd` | 单引号阻止展开,只传字面量 $@ |
| `echo $@ | cmd` | Word Splitting 导致参数边界丢失 |
| `echo "$@" | cmd` | 所有参数合并为一个字符串 |
$10 |
被解析为 $1 + 0 |
${10} |
for i in $*; do |
空格参数被拆分 | for i in "$@"; do |
cmd $var |
变量含空格时被拆分 | cmd "$var" |
set -a -b |
-a 被解析为选项 |
set -- -a -b |
六、总结
Shell 位置参数的传递看似简单,实则暗藏玄机。核心要点:
-
单引号 vs 双引号 :单引号是"强引用",内部一切字符都是字面量;双引号是"弱引用",允许
$展开。echo '$@'中的$@不会被展开。 -
"$@"是唯一安全的全参数传递方式:它能保留每个参数的独立性和内部空格,是编写健壮脚本的基石。 -
管道创建子 Shell :管道符
|右侧的命令在子 Shell 中执行,对变量的修改不会反映到父 Shell。 -
Word Splitting 是隐形杀手:不加引号的变量展开会触发单词拆分,导致含空格的参数被撕裂。
-
printf '%s\n' "$@"是管道传参的标准解法 :当需要将多个参数通过管道传递时,用printf每个参数单独输出一行,再配合xargs -I {}或xargs -0处理。
掌握这些规则后,那些让人"怀疑人生"的案例就不再神秘了。