Here-Document的<< 与 <<- 与 <<< 多解说笔记250722
实测
自测代码: (脚本必须将 CRLF 换成 LF )
#!/bin/bash
tee << EOF
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
EOF
tee <<< "三个小于号(<<<)用于单行, 在这里既用于展示<<<的用法, 也用于分割上面↑↑↑👆👆👆和下面↓↓↓👇👇👇的内容 ------ 顺便实验Unicode字符兼容性"
tee <<- EOF
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
EOF
在 fedora 42 上的结果: <<的空格和tab都保持原样, <<-的空格保持原样, tab被<<-吃掉了
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
三个小于号(<<<)用于单行, 在这里既用于展示<<<的用法, 也用于分割上面↑↑↑👆👆👆和下面↓↓↓👇👇👇的内容 ------ 顺便实验Unicode字符兼容性
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
在 Ubuntu24.04 上的结果: <<的空格和tab都保持原样, <<-的空格保持原样, tab被<<-吃掉了
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
三个小于号(<<<)用于单行, 在这里既用于展示<<<的用法, 也用于分割上面↑↑↑👆👆👆和下面↓↓↓👇👇👇的内容 ------ 顺便实验Unicode字符兼容性
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
在 AlmaLinux9.6 上的结果: <<的空格和tab都保持原样, <<-的空格保持原样, tab被<<-吃掉了
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
三个小于号(<<<)用于单行, 在这里既用于展示<<<的用法, 也用于分割上面↑↑↑👆👆👆和下面↓↓↓👇👇👇的内容 ------ 顺便实验Unicode字符兼容性
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
在 Debian10.12 上的结果: <<的空格和tab都保持原样, <<-的空格保持原样, tab被<<-吃掉了
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
三个小于号(<<<)用于单行, 在这里既用于展示<<<的用法, 也用于分割上面↑↑↑👆👆👆和下面↓↓↓👇👇👇的内容 ------ 顺便实验Unicode字符兼容性
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
在 Debian12 上的结果: <<的空格和tab都保持原样, <<-的空格保持原样, tab被<<-吃掉了
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
三个小于号(<<<)用于单行, 在这里既用于展示<<<的用法, 也用于分割上面↑↑↑👆👆👆和下面↓↓↓👇👇👇的内容 ------ 顺便实验Unicode字符兼容性
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
在 Ubuntu16.4.7 上的结果: <<的空格和tab都保持原样, <<-的空格保持原样, tab被<<-吃掉了
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
三个小于号(<<<)用于单行, 在这里既用于展示<<<的用法, 也用于分割上面↑↑↑👆👆👆和下面↓↓↓👇👇👇的内容 ------ 顺便实验Unicode字符兼容性
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
在 Ubuntu18.04.6 上的结果: <<的空格和tab都保持原样, <<-的空格保持原样, tab被<<-吃掉了
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
三个小于号(<<<)用于单行, 在这里既用于展示<<<的用法, 也用于分割上面↑↑↑👆👆👆和下面↓↓↓👇👇👇的内容 ------ 顺便实验Unicode字符兼容性
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
在 CentOS7 上的结果: <<的空格和tab都保持原样, <<-的空格保持原样, tab被<<-吃掉了
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
三个小于号(<<<)用于单行, 在这里既用于展示<<<的用法, 也用于分割上面↑↑↑👆👆👆和下面↓↓↓👇👇👇的内容 ------ 顺便实验Unicode字符兼容性
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
在 CentOS6 上的结果: <<的空格和tab都保持原样, <<-的空格保持原样, tab被<<-吃掉了
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
三个小于号(<<<)用于单行, 在这里既用于展示<<<的用法, 也用于分割上面↑↑↑👆👆👆和下面↓↓↓👇👇👇的内容 ------ 顺便实验Unicode字符兼容性
0BlankBefore
2BlankBefore
4BlankBefore
6BlankBefore
8BlankBefore
0TabBefore
2TabBefore
4TabBefore
6TabBefore
8TabBefore
<< 与 <<-
<< vs <<-:Here Document 的缩进处理详解
在 Linux Shell 中,<< 和 <<- 都用于创建 Here Document(嵌入文档),但它们在缩进处理上有本质区别:
核心区别对比
| 特性 | << (标准形式) | <<- (缩进处理形式) |
|---|---|---|
| 缩进处理 | 保留所有空白(含缩进) | 仅去除前导制表符(Tab) |
| 结束标记要求 | 必须顶格无缩进 | 允许前有制表符 |
| 空格处理 | 保留空格 | 不处理空格 |
| 典型用例 | 需要保留格式的内容 | 美化脚本中的长文档 |
| 兼容性 | 所有 POSIX Shell | 所有 POSIX Shell |
| 内容完整性 | 完全保留原始格式 | 可能改变缩进结构 |
详细解析
1. << (标准 Here Document)
- 行为:完全保留所有空白字符
- 结束标记:必须严格顶格(无空格/Tab)
- 示例:
输出:cat << END 保留所有缩进 空格也保留 END # 必须顶格保留所有缩进 空格也保留
2. <<- (缩进处理 Here Document)
- 行为:仅去除制表符(Tab) 缩进
- 结束标记:允许有前导 Tab
- 关键限制:不处理空格!
- 示例:
输出:cat <<- TABBED 第一行(Tab缩进) 混合缩进:Tab+空格 TABBED # 前有Tab第一行(Tab缩进) 混合缩进:Tab+空格 # 空格保留
关键注意事项
1. Tab 与空格的区别
# 正确:Tab 缩进会被移除
cat <<- EOF
Tab缩进内容
EOF # 前有Tab → 有效
# 错误:空格缩进不会被处理
cat <<- EOF
空格缩进内容 # 4个空格
EOF # 前有空格 → 无效(报错)
2. 混合缩进问题
cat <<- MIXED
Tab行 → 缩进移除
空格行 → 保留空格
MIXED # 前有Tab
输出:
Tab行 → 缩进移除
空格行 → 保留空格 # 4空格保留
3. 嵌套使用
if true; then
# 外层缩进
cat <<- INNER
内层Tab缩进
INNER # 前有Tab
fi
输出:内层Tab缩进(无缩进)
使用场景指南
何时用 << ✅
# 1. 需要保留格式的文档
cat << FORMATTED > poem.txt
静夜思
床前明月光
疑是地上霜
FORMATTED
# 2. 空格敏感的配置
cat << SPACE > config.yml
indentation:
level: 4 # 必须4空格
SPACE
何时用 <<- ✅
# 1. 脚本美化(使用Tab缩进)
if [ "$verbose" = true ]; then
cat <<- VERBOSE
[DEBUG] 详细日志:
用户: $USER
时间: $(date)
VERBOSE
fi
# 2. SQL查询(可读性优先)
mysqldump <<- SQL
SELECT *
FROM users
WHERE active=1
SQL # 结束标记对齐
何时避免 <<- ⚠️
# 1. 需要保留Tab的场景
cat <<- TABS > tabs.txt
重要制表符: → 这里Tab会被保留
但行首Tab会被移除
TABS
# 2. 空格缩进的环境
# (如Python脚本)
技术原理图解
graph TD
A[Here Document] --> B[<<]
A --> C[<<-]
B --> D[保留所有空白]
B --> E[结束标记必须顶格]
C --> F[去除前导Tab]
C --> G[结束标记允许Tab]
C --> H[不处理空格]
D --> I[格式敏感场景]
E --> J[可能破坏脚本缩进]
F --> K[美化脚本]
G --> K
H --> L[空格问题]
最佳实践
-
统一缩进风格:
# 推荐:全脚本使用Tab缩进 function show_info() { cat <<- INFO 系统信息: 用户: $USER 时间: $(date) INFO } -
禁用替换的缩进文档:
# 单引号+缩进处理 cat <<- 'SQL' > query.sql SELECT * FROM ${table} # 原样保留 SQL -
错误处理技巧:
# 检查结束标记是否有效 if ! cat <<- TEST 内容(4空格) TEST # 故意用空格缩进 then echo "错误:结束标记前有空格!" >&2 exit 1 fi -
多级缩进方案:
main() { local indent=" " # 4空格 cat <<- EOF | sed "s/^/$indent/" 一级缩进内容 EOF }
<< 与 <<<
<< vs <<<:多行文档 vs 单行字符串输入详解
在 Linux Shell 中,<<(Here Document)和 <<<(Here String)都是输入重定向操作符,但它们在输入类型、处理方式和使用场景上有本质区别:
核心对比表
| 特性 | << (Here Document) | <<< (Here String) |
|---|---|---|
| 输入类型 | 多行文档 | 单行字符串 |
| 语法 | cmd << DELIM内容行...DELIM | cmd <<< "字符串" |
| 结束标记 | 必需 | 不需要 |
| 变量替换 | 默认启用(除非定界符被引用) | 总是启用 |
| 末尾换行符 | 保留原始换行 | 自动添加一个换行符 |
| 内存占用 | 较高(生成临时文件/管道) | 较低(直接传递字符串) |
| POSIX 标准 | ✔️ 完全兼容 | ❌ Bash/Zsh 扩展 |
| 典型用例 | 脚本、配置、SQL 等结构化文本 | 命令行参数、简单字符串处理 |
详细解析
1. << (Here Document)
- 行为:创建临时多行输入流
- 处理流程:
graph LR A[输入内容] --> B[创建临时文件/管道] B --> C[命令读取] C --> D[执行变量替换] D --> E[输出结果] - 示例:
输出:# 多行文档输入 cat << EOF 第一行 第二行: $HOME EOF第一行 第二行: /home/user
2. <<< (Here String)
- 行为:直接传递单行字符串
- 处理流程:
graph LR A[字符串] --> B[添加换行符] B --> C[直接传递到命令] C --> D[立即处理] - 示例:
输出:# 单行字符串输入 wc -c <<< "Hello" # 计算字节数(包含自动添加的换行符)6("Hello" 5字节 + \n 1字节)
关键区别演示
1. 输入结构差异
# << 保留多行结构
cat << DOC
Line 1
Line 2
DOC
# <<< 视为单行
cat <<< "Line 1
Line 2" # 输出: Line 1\nLine 2
2. 换行符处理
# << 保留原始换行
tr '\n' ':' << END
a
b
END # 输出: a:b:
# <<< 自动添加换行
tr '\n' ':' <<< "text" # 输出: text:
3. 性能差异
# 处理大文本时 <<< 更高效
time for i in {1..1000}; do
cat <<< "$(seq 1 1000)"
done > /dev/null
time for i in {1..1000}; do
cat << END
$(seq 1 1000)
END
done > /dev/null
<<<版本通常快 2-3 倍
使用场景指南
何时用 << ✅
# 1. 生成配置文件
cat > app.conf << CONFIG
[server]
port=8080
log_dir=/var/log
CONFIG
# 2. 执行多行命令
ssh user@host << SSH_CMD
cd /app
git pull
sudo systemctl restart service
SSH_CMD
# 3. 保留格式的文本
cat << POEM
静夜思
床前明月光
疑是地上霜
POEM
何时用 <<< ✅
# 1. 快速字符串处理
base64 <<< "encode this" # 编码
md5sum <<< "text" # 计算哈希
# 2. 避免管道操作
grep "error" <<< "$log_content" # 优于 echo "$log" | grep
# 3. 命令行参数传递
docker exec -i container sh <<< "ls -l"
# 4. 数学计算
bc <<< "2^20" # 计算 2 的 20 次方
何时避免 ⚠️
# << 不适合
echo "单行内容" | cmd # 简单单行用管道更直观
# <<< 不适合
cat <<< "多行
内容" # 多行文本应使用 <<
进阶技巧
1. 混合使用
# 动态生成 SQL 查询
query=$(cat << SQL
SELECT * FROM users
WHERE name="$1"
SQL
)
mysql <<< "$query" # 执行查询
2. 禁用替换的 Here String
# 使用单引号禁用替换
cat <<< '$HOME' # 输出 $HOME
3. 二进制数据处理
# <<< 处理二进制 (base64)
base64 -d <<< "SGVsbG8K" # 解码后输出 Hello
4. 性能优化
# 避免多次调用 <<<
mapfile -t arr <<< "$(seq 1 100)" # 一次读入数组
特殊注意事项
-
换行符陷阱:
# <<< 自动添加换行 printf "%s" "$(seq 3)" | wc -c # 输出 3 wc -c <<< "$(seq 3)" # 输出 4 (1+2+3+\n) -
变量作用域:
# << 在新子shell中执行替换 var=outer cat << 'END' $var # 输出 outer END -
兼容性解决方案:
# 替代 <<< 的 POSIX 方法 echo "string" | cmd # 标准管道 cmd << EOF # Here Document string EOF -
大文本处理:
# 超过 64KB 的文本避免用 <<< # 改用临时文件或 <<
通过理解这些核心区别,您可以根据具体场景选择最高效、最合适的输入重定向方式。多行结构化内容首选 <<,单行字符串处理则优先考虑 <<<。
<<、<<-、<<< 详解
<<、<<-、<<< 详解
在 Linux Shell 中,这三个运算符都与输入重定向相关,但功能和行为有显著差异:
1. << (标准 Here Document)
- 作用:创建多行输入文档
- 语法:
command << DELIMITER 文档内容... DELIMITER - 特点:
- 结束标记
DELIMITER必须单独一行且无缩进 - 默认执行变量/命令替换(除非定界符被引用)
- 保留所有空白字符和缩进
- 结束标记
- 示例:
输出:cat << END 第一行 第二行: $HOME END第一行 第二行: /home/user
2. <<- (带缩进处理的 Here Document)
- 作用:允许缩进的 Here Document
- 语法:
command <<- DELIMITER 文档内容... DELIMITER # 前导制表符会被忽略 - 关键特性:
- 仅忽略制表符(Tab) 缩进(空格无效)
- 结束标记前可以有 Tab
- 提升脚本可读性
- 示例:
输出(无缩进):cat <<- INDENTED 第一行(有Tab缩进) 第二行(有Tab缩进) INDENTED # 前有Tab第一行(有Tab缩进) 第二行(有Tab缩进)
3. <<< (Here String)
- 作用:提供单行字符串输入
- 语法:
command <<< "字符串内容" - 特点:
- 适用于单行输入
- 自动执行变量/命令替换
- 无需结束标记
- 末尾自动添加换行符
- 示例:
# 单行字符串输入 wc -c <<< "Hello World" # 计算字节数(包含换行符) # 带变量替换 name="Alice" grep "li" <<< "Hello $name" # 输出 Hello Alice
三者的核心区别
| 特性 | << | <<- | <<< |
|---|---|---|---|
| 输入类型 | 多行文档 | 带缩进的多行文档 | 单行字符串 |
| 结束标记 | 必需(DELIMITER) | 必需(DELIMITER) | 不需要 |
| 缩进处理 | 保留所有空白 | 忽略前导制表符(Tab) | 不适用 |
| 变量替换 | 默认启用(除非引用) | 默认启用(除非引用) | 总是启用 |
| 自动换行符 | 保留原始换行 | 保留原始换行 | 末尾自动添加换行符 |
| 典型用例 | 配置文件、SQL查询 | 美化脚本中的长文档 | 单行命令输入 |
| POSIX 兼容性 | ✔️ 完全兼容 | ✔️ 完全兼容 | ❌ Bash/Zsh 扩展 |
使用场景对比
1. 多行内容 → 用 << 或 <<-
# 生成配置文件 (<<)
cat > app.conf << CONFIG
server_port=8080
log_dir=/var/log
CONFIG
# 带缩进的脚本 (<<-)
if true; then
cat <<- EOF
缩进内容
可读性更好
EOF # 前有Tab
fi
2. 单行内容 → 用 <<<
# 快速字符串处理
base64 <<< "encode this" # 编码字符串
md5sum <<< "text" # 计算MD5
# 避免管道和echo
grep "error" <<< "$logs" # 直接搜索变量内容
3. 混合使用
# Here Document + Here String
sql_query=$(cat << EOF
SELECT * FROM users
WHERE name="$1"
EOF
)
mysql <<< "$sql_query" # 执行SQL
特殊注意事项
-
<<-的陷阱:# 错误:空格缩进不会被忽略 cat <<- EOF 空格缩进内容 # 保留空格 EOF # 必须用Tab缩进 -
<<<的换行符:# 包含换行符计数 str="hello" wc -c <<< "$str" # 输出 6 (hello + \n) -
性能差异:
# <<< 比管道更高效 tr 'a-z' 'A-Z' <<< "text" # 优于 echo "text" | tr ... -
嵌套技巧:
# 嵌套 Here Document cat << 'OUTER' | ssh remote cat << INNER 内部文档 INNER OUTER
验证脚本
#!/bin/bash
echo "===== << 示例 ====="
cat << STANDARD
标准文档
保留缩进: $HOME
STANDARD
echo "===== <<- 示例 ====="
cat <<- STRIPPED
Tab缩进文档
保留内容缩进
STRIPPED # 前有Tab
echo "===== <<< 示例 ====="
wc -c <<< "测试字符串"
输出:
===== << 示例 =====
标准文档
保留缩进: /home/user
===== <<- 示例 =====
Tab缩进文档
保留内容缩进
===== <<< 示例 =====
14 # 中文字符+换行符的字节数
在 Linux Shell 中,<<、<<- 和 <<< 是三种不同的输入重定向操作符,各有特定的用途和行为。以下是详细解析和对比:
1. << (Here Document)
- 用途:传递多行文本块给命令
- 语法:
command << DELIMITER 文本行1 文本行2 ... DELIMITER - 特性:
- 保留所有空白字符(包括缩进)
- 默认执行变量/命令替换(除非用
'DELIMITER'禁用) - 终止标记
DELIMITER必须独占一行且无前导空格
- 示例:
输出:cat << END Line 1 # 缩进被保留 Line 2 ENDLine 1 Line 2
2. <<- (Here Document with Tab Suppression)
- 用途:传递多行文本块,忽略行首的制表符(Tab)
- 语法:
command <<- DELIMITER 文本行1 # 行首Tab被忽略 文本行2 DELIMITER - 特性:
- 只忽略制表符(Tab),不忽略空格
- 终止标记前的 Tab 也会被忽略(允许缩进终止符)
- 变量/命令替换行为同
<<
- 示例:
输出:cat <<- END Tabbed Line 1 # 行首Tab被删除 Tabbed Line 2 END # 终止符可缩进Tabbed Line 1 Tabbed Line 2 - 注意:混合空格和 Tab 时,仅 Tab 被删除:
输出:cat <<- END Space + Tab→Line # 仅删除Tab,保留空格 ENDSpace + Tab→Line # 开头的4个空格保留
3. <<< (Here String)
- 用途:传递单行字符串给命令
- 语法:
command <<< "字符串内容" - 特性:
- 自动在字符串末尾添加换行符(类似
echo) - 支持变量/命令替换
- 比管道
echo "str" | command更高效(避免子shell)
- 自动在字符串末尾添加换行符(类似
- 示例:
# 基本用法 wc -c <<< "Hello" # 统计字节数(包含换行符) # 输出:6("Hello" + \n) # 变量替换 name="Alice" grep "li" <<< "$name" # 输出:Alice # 命令替换 awk '{print $2}' <<< "Apple Banana Cherry" # 输出:Banana
三者的核心对比
| 特性 | << (Here Document) | <<- (Tab-Suppressed) | <<< (Here String) |
|---|---|---|---|
| 输入类型 | 多行文本块 | 多行文本块 | 单行字符串 |
| 行首空白处理 | 保留所有空白 | 仅删除行首 Tab | 不适用(单行) |
| 终止符要求 | 必须独占一行(无缩进) | 可缩进(仅限 Tab) | 不需要终止符 |
| 变量替换 | 支持(除非 'EOF') | 支持(除非 'EOF') | 支持 |
| 末尾换行符 | 按原样保留 | 按原样保留 | 自动添加 |
| 典型应用场景 | 生成配置文件、多行脚本 | 缩进美观的多行文本 | 单行文本处理、避免管道 |
使用场景详解
<< 最佳场景:多行内容处理
# 生成配置文件
cat > app.conf << CONFIG
server {
port=8080
host="$(hostname)"
}
CONFIG
# 执行多行SQL
mysql << SQL
USE mydb;
SELECT * FROM users;
SQL
<<- 最佳场景:脚本内美化缩进
function deploy() {
ssh server <<- DEPLOY
cd /app
git pull
systemctl restart app
DEPLOY # 终止符可对齐缩进
}
<<< 最佳场景:单行字符串处理
# 检查字符串是否包含子串
grep "error" <<< "$log_msg"
# 数学计算(避免管道)
bc <<< "2^20" # 计算2的20次方
# 快速测试命令
awk '{print toupper($0)}' <<< "hello" # 输出:HELLO
特殊注意事项
-
<<-的陷阱:- 只处理 Tab 字符(ASCII 0x09),不处理空格
- 在编辑器中将 Tab 自动转为空格会导致功能失效
- 推荐在脚本中显式使用
Ctrl+V Tab输入真实 Tab
-
<<<的换行符:echo -n "Hello" | wc -c # 输出:5(无换行) wc -c <<< "Hello" # 输出:6(含\n) wc -c <<< -n "Hello" # 错误!<<< 不支持选项 -
性能对比:
# 高效方式(无子shell) md5sum <<< "$data" # 低效方式(创建子shell) echo "$data" | md5sum -
嵌套使用:
# 混合使用示例 cat << EOF1 Outer Document $(cat << EOF2 Inner Document EOF2 ) EOF1
总结选择指南
| 需求 | 推荐操作符 | 示例 |
|---|---|---|
| 传递多行文本(保留格式) | << | cat << END |
| 多行文本需代码缩进 | <<- | cat <<- EOF(使用 Tab 缩进) |
| 传递单行字符串 | <<< | grep "pat" <<< "$var" |
| 避免管道开销 | <<< | wc -l <<< "$lines" |
| 脚本内美观的多行代码块 | <<- | 函数内缩进的 Here Document |
掌握这三者的区别能显著提升 Shell 脚本的可读性和效率,特别是在处理配置文件生成、远程命令执行和字符串操作时。
在 Linux Shell 中,<<、<<- 和 <<< 是三种不同的重定向操作符,各有特定用途。以下是详细对比:
1. <<(标准 Here Document)
- 作用:将多行文本作为命令的标准输入
- 语法:
命令 << 分隔符 - 特点:
- 文本以单独一行的分隔符结束
- 结束分隔符必须顶格写(不能有缩进)
- 支持变量/命令替换(除非用
<< 'EOF')
- 示例:
cat << END Line 1 Line 2 END
2. <<-(带缩进的 Here Document)
- 作用:允许结束分隔符前有 Tab 缩进
- 语法:
命令 <<- 分隔符 - 特点:
- 仅忽略 Tab 缩进(空格无效)
- 结束分隔符前可以有多个 Tab
- 文本内容中的缩进会被保留
- 示例:
输出:cat <<- EOF This line has Tab indent # 实际输出会保留缩进 EOF # 结束符前有 Tab(会被忽略)This line has Tab indent
3. <<<(Here String)
- 作用:将单行字符串作为命令的标准输入
- 语法:
命令 <<< "字符串" - 特点:
- 处理单行内容(非多行)
- 字符串末尾自动添加换行符
- 支持变量替换和命令替换
- 示例:
grep "hello" <<< "hello world" # 输出: hello world wc -l <<< "$(ls)" # 统计当前目录文件数
核心对比表
| 特性 | << (Here Document) | <<- (缩进 Here Doc) | <<< (Here String) |
|---|---|---|---|
| 输入类型 | 多行文本 | 多行文本 | 单行字符串 |
| 结束标记 | 需单独行分隔符 | 需单独行分隔符 | 无结束标记 |
| 缩进处理 | 结束符必须顶格 | 可忽略结束符前 Tab | 不适用 |
| 变量替换 | ✅ (默认) | ✅ (默认) | ✅ |
| POSIX 标准 | ✅ | ✅ | ❌ (Bash 扩展) |
| 典型场景 | 生成配置文件 | 美化脚本缩进 | 单行命令输入 |
使用场景示例
1. <<(多行配置生成)
cat > app.conf << CONFIG
server {
port: 8080
host: $HOSTNAME
}
CONFIG
2. <<-(带缩进的脚本)
if true; then
cat <<- EOF
Indented message
Current user: $USER
EOF # 结束符前有 Tab
fi
3. <<<(单行字符串处理)
# 快速字符串处理
base64 <<< "hello" # 编码字符串
md5sum <<< "secret" # 计算 MD5
grep "error" <<< "$logs" # 搜索变量内容
重要注意事项
-
缩进规则:
<<-只忽略 Tab,不忽略空格
# 错误示例(使用空格缩进) cat <<- EOF This will FAIL # 空格缩进 EOF # 不会被识别为结束符 -
变量作用域:
# Here Document 在子 shell 执行 var="outer" cat << EOF | grep "inner" var="inner" # 修改的 var 不影响父 shell EOF echo $var # 输出: outer -
性能差异:
<<<比echo |管道更高效(避免创建子进程)
# 更高效的单行处理 wc -c <<< "test" # 优于 echo "test" | wc -c
最佳实践:
- 需要多行内容 → 用
<<或<<-- 需要单行内容 → 用
<<<- 脚本美化缩进 → 用
<<-+ Tab 缩进
在 Linux Shell 中,<<、<<- 和 <<< 是三种不同的输入重定向操作符,各自有特定的用途和行为:
1. <<(标准 Here Document)
- 作用:将多行文本块重定向到命令的标准输入
- 语法:
command << DELIMITER text line 1 text line 2 DELIMITER - 特点:
- 结束标记
DELIMITER必须单独成行且前后无空格 - 默认会展开变量(
$var)和命令替换($(cmd)) - 支持转义字符(如
\$防止变量展开) - 不忽略缩进(所有空白字符都会保留)
- 结束标记
示例:
cat << END
This indentation # 缩进会被保留
User: $USER # 变量会被展开
END
2. <<-(带缩进处理的 Here Document)
- 作用:允许缩进 Here Document 内容(忽略前导制表符)
- 语法:
command <<- DELIMITER text line 1 text line 2 DELIMITER - 特点:
- 忽略内容行的前导制表符(Tab)
- 结束标记行也可以用制表符缩进
- 只能忽略 Tab,不能忽略空格
- 其他行为与标准
<<相同(会展开变量)
示例:
cat <<- EOF
Indented with Tab # 前导 Tab 会被删除
User: $USER # 变量仍会被展开
EOF # 结束标记可缩进(必须用 Tab)
3. <<<(Here String)
- 作用:将单行字符串重定向到命令的标准输入
- 语法:
command <<< "string" - 特点:
- 处理单行输入(不是文本块)
- 字符串中变量和命令替换会被展开
- 末尾自动添加换行符(除非使用
-n选项) - 不需要结束标记
示例:
grep "root" <<< "$(cat /etc/passwd)"
wc -l <<< "one two three" # 输出 1(统计行数)
三者的对比总结
| 特性 | << (Here Document) | <<- (Indented Here Doc) | <<< (Here String) |
|---|---|---|---|
| 输入类型 | 多行文本块 | 多行文本块 | 单行字符串 |
| 结束标记 | 必需 (如 EOF) | 必需 (如 EOF) | 不需要 |
| 变量展开 | ✅ 默认展开 | ✅ 默认展开 | ✅ 默认展开 |
| 忽略缩进 | ❌ 保留所有空白 | ✅ 仅忽略前导 Tab | ❌ 不适用 |
| 自动添加换行符 | ❌ 按原样输出 | ❌ 按原样输出 | ✅ 总是添加 |
| 典型用途 | 多行配置/脚本嵌入 | 缩进的多行内容 | 单行命令输入 |
使用场景示例
<<(标准 Here Document):
# 生成配置文件
cat > app.conf << CONFIG
server {
listen 80;
root $WEB_ROOT; # 变量展开
}
CONFIG
<<-(缩进 Here Document):
# 带缩进的脚本(使用 Tab 缩进)
if [ "$debug" = true ]; then
logger <<- LOG
[DEBUG] Process started
User: $USER
Time: $(date)
LOG # 缩进的结束标记
fi
<<<(Here String):
# 单行字符串处理
md5sum <<< "hello world" # 计算字符串哈希
grep "error" <<< "$log_data" # 在变量中搜索
bc <<< "2^20" # 计算 2 的 20 次方
重要注意事项
-
缩进规则:
<<-只忽略制表符(Tab),不忽略空格- 在脚本中必须使用真正的 Tab 字符,而非空格
-
引号处理:
# 禁止变量展开 cat << 'EOF' $PATH # 原样输出 EOF # Here String 的引号 cat <<< '$PATH' # 输出: $PATH cat <<< "$PATH" # 输出: 路径内容 -
换行符差异:
printf "no newline" | wc -c # 10 字符 wc -c <<< "no newline" # 11 字符(自动添加 \n)
根据需求选择合适的操作符:
- 需要多行输入 → 用
<<或<<- - 需要保持脚本缩进美观 → 用
<<-+ Tab - 快速传递单行字符串 → 用
<<<
在 Linux shell 中,<<、<<- 和 <<< 是三种不同的重定向操作符,用于处理输入数据(Here Document、Here String 等)。它们的核心区别在于输入来源和格式处理方式。以下是详细对比:
1. <<(Here Document)
功能
- 从当前脚本/命令行直接读取多行输入,直到遇到指定的结束标记(如
EOF)。 - 输入内容可以包含变量、命令替换等(除非用单引号
'EOF'禁用扩展)。
语法
command << EOF
输入内容...
EOF
特点
- 结束标记必须顶格(不能有空格或制表符前缀)。
- 默认允许变量扩展(如
$var)和命令替换(如$(date)),除非用<< 'EOF'或<< \EOF禁用。
示例
cat << EOF
Hello, $USER!
Today is $(date).
EOF
输出:
Hello, alice!
Today is Wed Oct 25 12:34:56 UTC 2023.
2. <<-(缩进 Here Document)
功能
- 与
<<类似,但允许忽略每行开头的制表符(Tab)(非空格!)。 - 适合需要代码缩进的场景(如嵌套在脚本块中时保持美观)。
语法
command <<- EOF
输入内容(每行前的Tab会被忽略)...
EOF
特点
- 仅忽略 Tab:空格前缀仍会被保留,可能导致错误。
- 其他行为与
<<完全一致(变量扩展、命令替换等)。
示例
if true; then
cat <<- EOF
This line is indented with tabs.
$USER is logged in.
EOF
fi
输出:
This line is indented with tabs.
alice is logged in.
3. <<<(Here String)
功能
- 将单个字符串作为输入传递给命令(类似于
echo "string" | command的简写)。 - 常用于快速传递变量或简单文本。
语法
command <<< "输入字符串"
# 或
command <<< $var # 变量内容作为输入
特点
- 单行输入:无法直接传递多行内容(需用
\n或换行符拼接)。 - 自动处理引号和变量扩展(与普通字符串行为一致)。
示例
# 传递字符串
grep "error" <<< "This is an error message."
# 传递变量
file_content=$(cat file.txt)
wc -c <<< "$file_content" # 统计文件字符数
关键区别总结
| 操作符 | 输入来源 | 多行支持 | 缩进处理 | 典型用途 |
|---|---|---|---|---|
<< | Here Document | ✅ 是 | ❌ 禁止前缀 | 传递多行文本(如配置、模板) |
<<- | Here Document | ✅ 是 | ✅ 忽略 Tab | 缩进的多行文本(如脚本块内) |
<<< | Here String | ❌ 否 | ❌ 无意义 | 快速传递单行字符串或变量 |
其他注意事项
-
Here Document 的结束标记:
- 必须单独一行且无前导字符(除非用
<<-忽略 Tab)。 - 标记名可以自定义(如
EOV、STOP等)。
- 必须单独一行且无前导字符(除非用
-
Here String 的变量扩展:
name="Alice" echo <<< "Hello, $name!" # 错误!需去掉 `echo` 或改用 `cat <<< "Hello, $name"` cat <<< "Hello, $name" # 正确:输出 "Hello, Alice" -
性能差异:
<<<比echo "string" | command更高效(少启动一个子进程)。<</<<-适合大量文本输入,<<<适合短输入。
实际应用场景
-
<<:生成配置文件、嵌入多行代码或文本模板。cat << EOF | sudo tee /etc/config.conf user = $USER path = /data/$USER EOF -
<<-:在if/for块中保持缩进美观。if [ -f file.txt ]; then cat <<- EOF File exists! Content: $(cat file.txt) EOF fi -
<<<:快速处理变量或单行输入。# 统计单词数 wc -w <<< "This is a test." # 传递变量给 `bc` 计算 bc <<< "2 + 2 * 3"
通过合理选择操作符,可以更高效地编写 shell 脚本并控制输入行为。
在 Linux Shell 脚本中,<<、<<- 和 <<< 是三种不同的 输入重定向操作符,用于将数据传递给命令的标准输入(stdin)。它们的核心区别在于对输入内容的处理方式和用途。以下是详细对比:
1. <<(Here Document)
-
用途:将多行文本(Here Document)传递给命令作为标准输入。
-
语法:
command << DELIMITER 多行文本内容... DELIMITER -
特点:
- 变量替换:默认启用变量替换(除非用单引号包裹
DELIMITER)。 - 结束标记:
DELIMITER必须严格匹配(前后不能有空格或 Tab)。 - 缩进处理:不忽略任何缩进(前导空格或 Tab 会被保留)。
- 变量替换:默认启用变量替换(除非用单引号包裹
-
示例:
VAR="World" cat << EOF Hello, $VAR! This is a Here Document. EOF输出:
Hello, World! This is a Here Document.
2. <<-(Here Document with Tab Stripping)
-
用途:与
<<类似,但允许在 Here Document 中忽略前导的 Tab 字符。 -
语法:
command <<- DELIMITER 多行文本内容... DELIMITER -
特点:
- 变量替换:默认启用变量替换(除非用单引号包裹
DELIMITER)。 - 结束标记:
DELIMITER必须严格匹配。 - 缩进处理:仅忽略前导的 Tab,空格不会被忽略。
- 变量替换:默认启用变量替换(除非用单引号包裹
-
示例:
VAR="World" cat <<- EOF Hello, $VAR! This line has leading tabs. EOF输出:
Hello, World! This line has leading tabs.
3. <<<(Here String)
-
用途:将单行字符串(Here String)传递给命令作为标准输入。
-
语法:
command <<< "单行字符串" -
特点:
- 变量替换:启用变量替换。
- 结束标记:无结束标记(直接由字符串本身界定)。
- 缩进处理:无缩进(仅适用于单行文本)。
-
示例:
VAR="Hello" cat <<< "$VAR, World!"输出:
Hello, World!
关键区别总结
| 操作符 | 用途 | 输入类型 | 变量替换 | 缩进处理 | 结束标记要求 |
|---|---|---|---|---|---|
<< | 多行文本 | 多行 | ✅ 启用 | 保留所有缩进 | 严格匹配 |
<<- | 多行文本(忽略 Tab) | 多行 | ✅ 启用 | 仅忽略前导 Tab | 严格匹配 |
<<< | 单行字符串 | 单行 | ✅ 启用 | 无缩进 | 无结束标记 |
注意事项
-
结束标记的格式:
<<和<<-的结束标记必须单独一行,且前后不能有空格或 Tab。- 示例错误:
cat << EOF Content... EOF # ❌ 错误:结束标记前有空格
-
变量替换控制:
- 如果希望禁用变量替换,可以用单引号包裹
DELIMITER:cat << 'EOF' $VAR will not be expanded. EOF
- 如果希望禁用变量替换,可以用单引号包裹
-
Here String 的限制:
<<<仅适用于单行输入,无法处理多行文本。- 示例错误:
cat <<< "Line 1 Line 2" # ❌ 错误:换行符会被截断
-
性能与适用场景:
<<和<<-适合处理多行文本(如配置文件、模板等)。<<<适合快速传递单行字符串(如调试或简单输入)。
实际应用场景
(1) 使用 << 生成多行文件
cat << EOF > config.txt
# This is a configuration file
HOST=localhost
PORT=8080
EOF
(2) 使用 <<- 处理带缩进的文本
cat <<- EOF > template.sh
#!/bin/bash
# This script is indented
echo "Indented content"
EOF
(3) 使用 <<< 快速传递单行输入
grep "error" <<< "This is an error message"
总结
<<:标准的 Here Document,适合多行文本。<<-:Here Document 的变体,适合处理带 Tab 缩进的多行文本。<<<:Here String,适合单行字符串输入。
根据需求选择合适的操作符,可以显著提升脚本的可读性和灵活性!
在Linux shell脚本中,<<、<<- 和 <<< 是三种不同的输入重定向符号,用于将文本传递给命令。它们的核心区别在于处理方式、文本范围和特殊字符的解析规则。以下是详细解析:
一、<<(Here-Document,标准形式)
基本语法
命令 << 分界符
多行文本内容
分界符
特点
- 多行文本:允许输入多行内容,直到遇到与起始分界符相同的行(分界符必须单独成行)。
- 变量替换:默认情况下,文本中的变量(如
$VAR)和命令(如$(cmd))会被解析。- 若需禁用替换,可将分界符用单引号包裹(如
'EOF')。
- 若需禁用替换,可将分界符用单引号包裹(如
- 严格格式:结束分界符必须顶格书写,前不能有任何空格或缩进。
示例
NAME="Alice"
cat << EOF
Hello $NAME
当前时间:$(date +%H:%M)
EOF
输出(假设当前时间为15:30):
Hello Alice
当前时间:15:30
二、<<-(Here-Document,忽略Tab缩进)
基本语法
命令 <<- 分界符
多行文本内容(可缩进)
分界符
特点
- 忽略Tab缩进:文本中所有行首的Tab字符(注意:空格不会被忽略)会被自动删除,使脚本更易读。
- 其他规则不变:变量替换、分界符匹配规则与
<<一致。
示例
cat <<- EOF
这行有Tab缩进
这行有两个Tab缩进
EOF # 结束分界符前的Tab也会被忽略
EOF
输出:
这行有Tab缩进
这行有两个Tab缩进
EOF
三、<<<(Here-String,单行文本)
基本语法
命令 <<< "单行文本内容"
特点
- 单行文本:只能传递一行文本,文本需用引号包裹(单引号或双引号)。
- 直接传递:文本会被直接作为命令的标准输入(等价于
echo "文本" | 命令)。 - 变量替换:双引号内的变量会被替换,单引号则保留原样。
示例
# 示例1:变量替换
NAME="Alice"
grep "Alice" <<< "$NAME" # 等价于 echo "Alice" | grep "Alice"
# 示例2:保留字面量
grep "$" <<< '$VAR' # 等价于 echo '$VAR' | grep "$"
四、对比总结
| 符号 | 类型 | 文本范围 | 变量替换 | 缩进处理 |
|---|---|---|---|---|
<< | Here-Document | 多行 | 默认替换,可禁用 | 分界符必须顶格,保留缩进 |
<<- | Here-Document(增强) | 多行 | 默认替换,可禁用 | 忽略行首Tab缩进 |
<<< | Here-String | 单行 | 引号内替换 | 不支持缩进,仅一行文本 |
五、典型应用场景
-
<<的场景- 生成配置文件(如Nginx、MySQL配置)。
- 向交互式命令批量输入(如
mysql << EOF ...)。
-
<<-的场景- 脚本中嵌入大段文本,需保持代码美观(如帮助信息、HTML模板)。
- 示例:
help() { cat <<- EOF 用法: script.sh [选项] -h 显示帮助 -v 显示版本 EOF }
-
<<<的场景- 单行文本处理(如传递参数给
grep、awk)。 - 简化管道操作(替代
echo | 命令)。
- 单行文本处理(如传递参数给
六、注意事项
- 分界符大小写敏感:
EOF和eof是不同的标记。 <<-仅忽略Tab:若使用空格缩进,分界符仍需顶格。- Here-String的引号:
<<< '文本'(单引号)禁用替换,<<< "文本"(双引号)启用替换。
通过合理选择这三种符号,可以更灵活地处理脚本中的文本输入,提高代码可读性和维护性。
在 Linux Shell 中,<<、<<- 和 <<< 是三种不同的输入重定向操作符,分别对应 多行文本块输入、可缩进的多行文本输入 和 单行字符串输入。以下是它们的核心区别及典型用法:
📊 核心功能对比
| 操作符 | 名称 | 输入类型 | 变量处理 | 典型场景 | 示例 |
|---|---|---|---|---|---|
<< | Here Document | 多行文本块 | 默认展开变量(除非加引号) | 生成配置文件、交互式命令输入 | cat << EOF多行内容EOF |
<<- | 缩进 Here Document | 多行文本块 | 同 << | 需缩进的多行文本(如脚本内使用) | cat <<- EOF·Tab缩进内容EOF |
<<< | Here String | 单行字符串 | 默认展开变量 | 快速传递单行字符串给命令 | grep "key" <<< "字符串" |
🔧 详细解析
1. <<(Here Document)
- 作用:将多行文本块作为命令的标准输入(stdin)。
- 变量处理:
- 无引号分隔符(如
<< EOF):文本中的$变量和$(命令)会被解析并替换。 - 单引号分隔符(如
<< 'EOF'):文本内容原样输出,禁止变量和命令替换。
- 无引号分隔符(如
- 示例:
cat << EOF Hello $USER # 输出 Hello <当前用户名> Today is $(date) EOF - 注意事项:
- 结束标记(如
EOF)必须顶格独占一行,前后无空格。
- 结束标记(如
2. <<-(缩进 Here Document)
- 作用:在
<<基础上忽略每行开头的 Tab 制表符(⚠️ 不忽略空格),便于脚本中缩进排版。 - 典型用例:在函数或循环中嵌入多行文本时保持代码可读性。
- 示例:
→ 输出:cat <<- EOF This line starts with Tab (ignored). EOF # 结束标记可用 Tab 缩进This line starts with Tab (ignored).(无缩进)。
3. <<<(Here String)
- 作用:将单行字符串作为命令的标准输入,无需文件或管道。
- 优势:比
echo "字符串" | command更简洁高效。 - 示例:
# 检查字符串是否包含 "key" grep "key" <<< "This is a key string" # 计算字符串的 SHA256 哈希 sha256sum <<< "text" - 变量处理:字符串中的变量默认展开:
name="Alice" grep "Hello" <<< "Hello $name" # 输出 Hello Alice
⚠️ 常见问题与避坑指南
-
<<-对空格无效- 仅删除行首的 Tab(
\t),空格会被保留,错误使用空格缩进会导致结束标记失效。
- 仅删除行首的 Tab(
-
结束标记冲突
- 若文本内容含
EOF,需更换唯一性标记(如END_CONFIG)。
- 若文本内容含
-
远程执行变量混淆
- 通过 SSH 执行时,本地变量需转义(
\$VAR)避免提前解析:。ssh user@host << EOF Local: $PWD # 本地解析 Remote: \$PWD # 远程解析 EOF
- 通过 SSH 执行时,本地变量需转义(
💎 总结:按场景选择
| 需求 | 推荐操作符 | 示例 |
|---|---|---|
| 动态生成多行配置(含变量) | << EOF | 生成 Nginx 配置 |
| 保留特殊字符的静态文本 | << 'EOF' | 输出 ${{TEMPLATE}} 模板 |
| 脚本内缩进的多行文本 | <<- EOF | 函数内嵌入 SQL 查询 |
| 快速传递单行字符串 | <<< | 校验字符串哈希值 |
通过合理选择操作符,可显著提升脚本可读性与执行效率。
在 Shell 脚本中,<<、<<- 和 <<< 都是用于输入重定向的操作符,但它们的行为和用途有显著区别。以下是详细解释:
1. <<(Here Document)
行为:
用于定义多行输入块(称为 here document),内容按原样传递到命令的标准输入,保留所有空格、缩进和换行。
语法:
command << DELIMITER
输入内容
DELIMITER
示例:
cat << EOF
Hello World
This is a multi-line text.
EOF
输出:
Hello World
This is a multi-line text.
特点:
- 默认保留内容中的缩进(如制表符、空格)。
- 定界符(如
EOF)必须单独一行且顶格书写。
2. <<-(Indented Here Document)
行为:
与 << 类似,但允许在定界符和内容前使用制表符(TAB)缩进。输入内容中的制表符会被自动去除(空格不会被去除)。
语法:
command <<- DELIMITER
输入内容(可用 TAB 缩进)
DELIMITER # 允许 TAB 缩进
示例:
cat <<- EOF
Hello World
This line is indented with a TAB.
EOF # 此处必须用 TAB 缩进
输出:
Hello World
This line is indented with a TAB.
特点:
- 仅支持 TAB 缩进(空格无效)。
- 适合代码格式化时对齐内容。
3. <<<(Here String)
行为:
将单行字符串直接传递到命令的标准输入(称为 here string)。会保留字符串中的转义字符和引号行为。
语法:
command <<< "字符串"
示例:
# 计算字符串的单词数
wc -w <<< "Hello Shell World"
输出:
3
特点:
- 比
echo "字符串" | command更高效(无需启动子进程)。 - 适用于单行输入或短文本。
三者的关键区别
| 操作符 | 输入类型 | 缩进处理 | 典型场景 |
|---|---|---|---|
<< | 多行块 | 保留所有缩进 | 多行文本(如生成配置文件) |
<<- | 多行块 | 去除行首 TAB 缩进 | 格式化的多行文本 |
<<< | 单行字符串 | 不处理缩进,直接传递 | 短文本快速输入 |
使用场景示例
场景 1:生成配置文件(保留缩进)
cat > config.yaml << EOF
server:
port: 8080
host: 0.0.0.0
EOF
场景 2:格式化的 SQL 脚本(自动去除缩进)
mysql <<- SQL
SELECT * FROM users
WHERE name = "John";
SQL
场景 3:快速计算字符串哈希值
md5sum <<< "password123"
常见误区
-
<<-无法处理空格缩进:
只有 TAB 缩进会被自动去除,空格会保留。 -
<<<的多行字符串问题:
若需传递多行内容,需显式包含换行符:grep "key" <<< "line1\nline2\nkey=value" -
定界符冲突:
Here document 的定界符避免使用常见符号(如EOF而非EOF#),否则可能意外截断输入。
根据需求选择操作符,能显著提升脚本的灵活性和可维护性。
在 Linux 的 Shell 脚本中,<<、<<- 和 <<< 是三种不同的输入重定向语法,用于将数据传递给命令。以下是它们的详细区别和用法:
1. << (Here Document)
-
作用:将多行文本作为输入传递给命令,直到遇到指定的结束标记(如
EOF)。 -
语法:
command << EOF 多行文本内容 EOF -
特点:
- 变量替换:文本中的变量(如
$HOME)和命令替换(如$(date))会被解析。 - 缩进敏感:结束标记
EOF必须单独成行且顶格书写(不能有缩进)。 - 灵活性:结束标记可以是任意合法标识符(如
END、STOP)。
- 变量替换:文本中的变量(如
-
示例:
cat << EOF 当前用户:$USER 系统时间:$(date) EOF输出:
当前用户:root 系统时间:Mon Jul 1 12:34:56 UTC 2024
2. <<- (Here Document with Tab Suppression)
-
作用:与
<<类似,但允许忽略行首的 制表符(Tab),方便代码缩进。 -
语法:
command <<- EOF 多行文本内容(行首可用 Tab 缩进) EOF -
特点:
- 仅忽略 Tab:仅删除行首的 Tab 字符(
\t),不处理空格。 - 结束标记仍需顶格:结束标记
EOF必须单独成行且无缩进(但文本内容可缩进)。
- 仅忽略 Tab:仅删除行首的 Tab 字符(
-
示例:
cat <<- EOF 当前用户:$USER 系统时间:$(date) EOF输出:
当前用户:root 系统时间:Mon Jul 1 12:34:56 UTC 2024
3. <<< (Here String)
-
作用:将单个字符串作为输入传递给命令,类似于
echo "内容" | command。 -
语法:
command <<< "字符串" -
特点:
- 单行输入:仅传递一行内容,适合快速传递变量或简单文本。
- 变量替换:字符串中的变量会被解析。
- 无结束标记:无需定义结束标记。
-
示例:
grep "root" <<< "$USER"输出:
root
关键区别总结
| 语法 | 输入来源 | 变量替换 | 缩进处理 | 结束标记 | 典型场景 |
|---|---|---|---|---|---|
<< | 多行文本 | ✅ 解析 | 敏感(需顶格) | 自定义(如 EOF) | 生成配置文件、多行输入 |
<<- | 多行文本 | ✅ 解析 | 忽略行首 Tab | 自定义(如 EOF) | 需要缩进的脚本(如 Makefile) |
<<< | 单行字符串 | ✅ 解析 | 不处理 | 无 | 快速传递变量或单行内容 |
使用场景建议
<<:适合需要多行输入且需解析变量的场景(如生成配置文件)。<<-:适合需要代码缩进的多行输入(如 Makefile 中的命令)。<<<:适合快速传递变量或单行内容,避免创建子 Shell(如grep搜索变量内容)。
通过合理选择语法,可以更高效地编写 Shell 脚本!
在 Linux Shell 中,<<、<<- 和 <<< 是三种不同的输入重定向操作符,分别用于处理多行输入、忽略缩进的多行输入和单行字符串输入。以下是它们的详细对比与使用场景:
1. <<(Here Document)
功能:
- 将多行文本传递给命令作为标准输入(stdin)。
- 分隔符(如
EOF)必须顶格书写,且前后一致。 - 变量替换:默认启用(类似双引号行为),除非用单引号包裹分隔符(如
<< 'EOF')。
语法:
command << delimiter
文档内容
delimiter
示例:
cat << EOF
Hello, World!
Today is $(date).
EOF
输出:
Hello, World!
Today is 2025-07-22 17:12:00
注意事项:
- 分隔符
EOF必须单独成行,且无前导空格或缩进。 - 如果希望保留变量原始值,使用
<< 'EOF'禁用变量替换:cat << 'EOF' Hello, $name! EOF
2. <<-(Here Document + 忽略缩进)
功能:
- 与
<<类似,但允许在 每行前忽略前导Tab缩进。 - 适用场景:脚本中需要对齐代码块,但希望保留 Here Document 的多行输入特性。
语法:
command <<- delimiter
文档内容(每行可包含前导 Tab)
delimiter
示例:
cat <<- EOF
This line has a leading Tab.
But the Tab is ignored.
EOF
输出:
This line has a leading Tab.
But the Tab is ignored.
注意事项:
- 仅忽略
Tab,不忽略空格(避免混用空格和Tab)。 - 分隔符
EOF必须顶格书写。
3. <<<(Here String)
功能:
- 将单行字符串直接作为输入传递给命令。
- 适用场景:快速传递单行文本,无需多行输入。
语法:
command <<< "字符串"
示例:
cat <<< "Hello, Here String!"
输出:
Hello, Here String!
注意事项:
- 仅支持单行输入,无法处理多行文本。
- 变量替换默认启用(类似双引号行为):
输出:name="Alice" cat <<< "Hello, $name!"Hello, Alice!
4. 对比总结
| 操作符 | 输入类型 | 是否支持多行 | 是否忽略缩进 | 变量替换 | 典型用途 |
|---|---|---|---|---|---|
<< | Here Document | ✅ | ❌ | ✅(默认) | 多行输入、动态生成内容 |
<<- | Here Document | ✅ | ✅(仅限 Tab) | ✅(默认) | 脚本中对齐多行输入 |
<<< | Here String | ❌ | ❌ | ✅(默认) | 快速传递单行字符串 |
5. 实际应用场景 5.1 动态生成多行配置文件
cat << EOF > config.txt
# Configuration File
username=admin
password=$PASS
EOF
5.2 远程执行脚本(避免变量提前解析)
ssh user@remote << 'EOF'
echo "Remote path: $PATH"
EOF
5.3 快速传递单行数据
wc -c <<< "This is a test string."
6. 常见错误及解决方案 6.1 分隔符未闭合
# 错误:缺少分隔符 EOF
cat << EOF
This is a line.
解决:确保分隔符 EOF 单独成行并正确闭合。
6.2 分隔符缩进问题
# 错误:分隔符 EOF 前有空格
cat << EOF
This line has leading spaces.
EOF
解决:分隔符必须顶格书写,或使用 <<- EOF 忽略 Tab 缩进。
6.3 混用空格和 Tab
# 错误:混用空格和 Tab 缩进
cat <<- EOF
This line uses spaces. # 不会被忽略
EOF
解决:<<- 仅忽略 Tab,避免混用空格和 Tab。
7. 总结
<<:标准的 Here Document,适用于多行输入,需注意分隔符格式。<<-:适合脚本中对齐多行输入,忽略每行前的Tab。<<<:快速传递单行字符串,无需分隔符。
根据需求选择合适的操作符,可以显著提升 Shell 脚本的可读性和效率。
在Shell脚本中,<<、<<- 和 <<< 是三种不同的输入重定向操作符,用于将文本内容传递给命令。它们的核心区别在于处理方式和应用场景。以下是详细对比:
1. <<(Here Document,嵌入文档)
作用:将多行文本作为命令的输入,直到遇到结束分界符。
语法:
命令 << 分界符
多行文本内容...
分界符
特点:
- 分界符必须单独一行,且行首不能有空格或Tab。
- 支持变量替换和命令替换(取决于分界符是否加引号)。
- 转义字符(如
\n)默认不解析,需配合echo -e或printf。
示例:
cat << EOF
Hello $USER # 变量被替换
Date: $(date +%F) # 命令被执行
Line 1\nLine 2 # \n 不被解释为换行
EOF
输出:
Hello root # 假设当前用户是root
Date: 2025-07-22
Line 1\nLine 2
2. <<-(Here Document with Indentation,带缩进的嵌入文档)
作用:与 << 类似,但允许结束分界符前带有 Tab缩进(空格无效),常用于脚本中保持代码整洁。
语法:
命令 <<- 分界符
多行文本内容...
分界符 # 可缩进(必须用Tab)
特点:
- 结束分界符前的 Tab缩进会被自动忽略,但内容中的Tab不会被处理。
- 内容中的 空格缩进不会被忽略,仅Tab有效。
示例:
if true; then
cat <<-EOF
第一行文本(前面有Tab)
第二行文本(前面有Tab)
EOF # 结束符前的Tab会被忽略
fi
输出:
第一行文本(前面有Tab)
第二行文本(前面有Tab)
3. <<<(Here String,嵌入字符串)
作用:将单个字符串作为命令的标准输入(stdin)。
语法:
命令 <<< "字符串内容"
特点:
- 字符串会被视为单行输入,自动添加换行符。
- 支持变量替换和命令替换。
示例:
# 等价于 echo "Hello world" | wc -c
wc -c <<< "Hello world" # 计算字符串长度(含换行符)
输出:
12 # "Hello world\n" 共12个字符
4. 关键区别总结
| 操作符 | 名称 | 输入形式 | 分界符要求 | 变量替换 | 缩进处理 |
|---|---|---|---|---|---|
<< | Here Document | 多行文本 | 分界符必须顶格 | ✅ | 无特殊处理 |
<<- | 带缩进的Here Document | 多行文本 | 分界符可Tab缩进 | ✅ | 内容中Tab保留,结束符Tab忽略 |
<<< | Here String | 单个字符串(单行) | 无分界符,直接跟字符串 | ✅ | 无(视为单行文本) |
5. 常见应用场景
<< 的场景
- 生成多行配置文件:
cat << EOF > /etc/nginx/conf.d/site.conf server { listen 80; server_name example.com; } EOF
<<- 的场景
- 脚本中保持代码缩进美观:
function generate_script() { cat <<-EOF #!/bin/bash echo "这是一个生成的脚本" EOF }
<<< 的场景
- 简化单行输入:
# 将字符串传递给grep grep "world" <<< "Hello world" # 输出: Hello world # 传递变量给命令 message="Hello" echo -e <<< "$message\nWorld" # 输出两行: Hello 和 World
总结
<<:最常用,适合多行文本输入,需注意分界符位置。<<-:适合脚本中保持缩进,结束分界符可Tab缩进。<<<:适合单行字符串输入,语法更简洁。
根据输入是多行还是单行、是否需要保留缩进以及是否需要变量替换来选择合适的操作符。
下面用一句话先给出结论,随后逐条展开说明。
一句话记忆:
<<=原始 here-doc;<<-=允许前导 TAB 缩进的 here-doc;<<<=一次性单行字符串(here-string)。
──────────────────
<< EOF(here-document)
- 把后续 多行 文本当作标准输入,直到遇到独占一行的定界符
EOF为止。 - 文本里的
$var、$(cmd)、\n等都会被展开/解释(除非定界符被引号包裹)。 - 每行前面的空格 / TAB 会原封不动地送进命令。
例:
cat << EOF
hello $USER
EOF
# 输出包含 4 个空格
──────────────────
2. <<- EOF(带减号的 here-document)
- 语法与
<<相同,但:
‑ 在收集输入时,丢弃每行的前导 TAB 字符(空格不行,只认 TAB)。
‑ 结束行的前导 TAB 也会被丢弃。 - 用途:让脚本本身缩进美观,而不影响最终输入内容。
例:
if true; then
cat <<- TAB
line1
line2
TAB
fi
# 输出:
# line1
# line2
# (左侧 TAB 被吃掉)
──────────────────
3. <<< "string"(here-string)
- 把单个单词或字符串直接变成命令的标准输入,只有一行。
- 相当于
echo -n "string" | cmd的简写,但更高效(无子进程)。 - 字符串受引号规则控制:
‑ 无引号 / 双引号 → 变量、命令展开;
‑ 单引号 → 纯文本。
例:
grep foo <<< "$USER:$HOME"
# 等价于 echo "$USER:$HOME" | grep foo
──────────────────
快速对照表
| 符号 | 行数 | 缩进处理 | 典型场景 |
|---|---|---|---|
<< | 多行 | 保留所有前导空白 | 需要多行且保持原始格式 |
<<- | 多行 | 去掉前导 TAB | 脚本里美观缩进 |
<<< | 单行 | 无(只有一行) | 快速传递单行字符串给命令 |
记住:
- 想写多行 →
<<或<<- - 想去掉 TAB 缩进 → 选
<<- - 只想喂一行 → 用
<<<
Shell脚本中<<、<<-和<<<重定向符号的区别和用法
Shell脚本中<<、<<-和<<<重定向符号的区别和用法
在Shell脚本中,输入重定向符号用于将数据传递给命令,而不是从文件或标准输入读取。符号<<、<<-和<<<各有特定用途,主要涉及“Here Document”和“Here String”机制。下面我将逐步解释它们的区别、用法和实际示例,确保内容清晰可靠。参考了Linux shell标准文档和常见实践。
1. <<(Here Document)
- 作用:将多行文本块作为输入传递给命令。文本块以指定的分隔符(delimiter)开始和结束,文本中的变量、命令替换等会被Shell解释执行。
- 语法:
command << DELIMITER 文本内容... DELIMITER - 特点:
- 分隔符(如
EOF)是大小写敏感的。 - 文本内容会保留所有格式(包括缩进和换行)。
- 变量扩展(如
$VAR)和命令替换(如$(date))会被执行。
- 分隔符(如
- 示例:
以下脚本输出带变量的问候语:输出:cat << EOF Hello, $USER! # $USER会被替换为当前用户名 Today is $(date). # $(date)会被替换为当前日期 EOFHere Document 常用于生成配置文件或交互式脚本。Hello, alice! Today is Mon Jul 1 12:00:00 UTC 2024.
2. <<-(Here Document with Stripping Leading Tabs)
- 作用:与
<<类似,但会自动去除文本块中每行的前导制表符(tabs)。这使得脚本代码更整洁,同时不影响输出内容。 - 语法:
command <<- DELIMITER 文本内容(可缩进)... DELIMITER - 特点:
- 只去除制表符(tab),不处理空格。如果文本使用空格缩进,则不会被去除。
- 分隔符前的
-符号是必须的。 - 适用于需要对齐代码但输出不需缩进的场景。
- 示例:
以下脚本使用缩进,但输出时去除前导制表符:输出(无缩进):cat <<- EOF Hello, $USER! Today is $(date). EOF这在编写可读性高的脚本时非常有用。Hello, alice! Today is Mon Jul 1 12:00:00 UTC 2024.
3. <<<(Here String)
- 作用:将单个字符串作为输入传递给命令,无需多行文本块。类似于简化的Here Document,但更适用于短字符串。
- 语法:
command <<< "字符串" - 特点:
- 字符串可以是变量或表达式,Shell会先执行扩展。
- 不需要分隔符,操作更简洁。
- 常用于替代
echo "string" | command的管道方式,减少子进程开销。
- 示例:
以下脚本用grep直接搜索字符串:或使用变量:grep "hello" <<< "hello world" # 输出: helloHere String 适合快速测试或单行数据处理。str="test" wc -c <<< "$str" # 计算字符串长度(包括换行符),输出: 5(因为"test"加换行符共5字节)
总结区别
| 符号 | 用途 | 数据处理 | 适用场景 |
|---|---|---|---|
<< | Here Document | 多行文本,扩展变量 | 生成文件、多行输入(如SQL查询) |
<<- | Here Document(去缩进) | 多行文本,去除前导制表符 | 美化脚本代码,保持输出整洁 |
<<< | Here String | 单行字符串,扩展变量 | 快速传递字符串,避免管道开销 |
关键区别:
<<和<<-处理多行文本,而<<<处理单行字符串。<<-是<<的变体,专为去除制表符缩进而设计。- 所有符号都支持变量扩展,但行为一致:双引号包围分隔符时,内容被视为字面值(不扩展)。
在 Linux Shell 中,<<、<<- 和 <<< 是用于输入重定向和多行文本处理的操作符,其核心区别在于输入格式、缩进处理以及适用场景。以下是详细对比与示例:
一、基本功能与语法
| 操作符 | 功能描述 | 语法示例 |
|---|---|---|
<< | Here Document,将多行文本作为命令输入,直到遇到结束标记(如 EOF) | command << EOF\n内容\nEOF |
<<- | 支持缩进的 Here Document,允许行首的制表符(Tab)被忽略 | command <<- EOF\n\t缩进内容\nEOF |
<<< | Here String,将单行字符串直接作为命令输入 | command <<< "字符串" |
二、行为差异与适用场景
1. <<(Here Document)
- 多行输入:适用于需要嵌入多行文本的场景(如配置文件、脚本交互)。
- 变量替换:默认启用变量和命令替换(除非用单引号包裹标记)。
- 结束标记:必须顶格书写,前后无空格。
- 示例:
输出文件内容:cat << EOF > config.txt server { listen 80; host example.com } EOFserver { listen 80; host example.com }
2. <<-(缩进 Here Document)
- 缩进支持:允许行首的制表符(Tab)被忽略,提升脚本可读性。
- 结束标记缩进:结束标记前的制表符会被删除,但空格无效。
- 示例:
输出:if [ true ]; then cat <<- EOF 缩进内容(Tab开头) EOF fi缩进内容
3. <<<(Here String)
- 单行输入:直接传递单行字符串,无需多行标记。
- 变量替换:支持变量替换(如
$VAR)。 - 示例:
输出:echo "Hello World" | grep "World" # 传统管道方式 grep "World" <<< "Hello World" # Here String 简化写法Hello World
三、核心区别总结
| 特性 | << | <<- | <<< |
|---|---|---|---|
| 输入类型 | 多行文本 | 多行文本(支持缩进) | 单行字符串 |
| 缩进处理 | 不支持 | 支持(仅制表符) | 不适用 |
| 变量替换 | 默认启用 | 默认启用 | 默认启用 |
| 结束标记要求 | 必须顶格 | 结束标记可缩进(Tab) | 无结束标记 |
| 典型用途 | 配置文件、交互命令 | 缩进代码块 | 快速传递字符串 |
四、实战场景对比 场景 1:生成配置文件(多行内容)
# 使用 `<<`
cat << EOF > nginx.conf
server {
listen 80;
server_name localhost;
}
EOF
# 使用 `<<-`(缩进代码块)
cat <<- EOF > nginx.conf
server {
listen 80;
server_name localhost;
}
EOF
输出文件内容相同,但 <<- 允许代码缩进,提升可读性。
场景 2:单行字符串处理
# 使用 `<<<` 快速传递参数
wc -w <<< "This is a test string"
# 输出:5
# 等效的传统写法
echo "This is a test string" | wc -w
场景 3:自动化交互命令
# 使用 `<<` 向 FTP 服务器发送指令
ftp -n << EOF
open ftp.example.com
user admin password
get file.txt
bye
EOF
# 使用 `<<<` 传递密码(不推荐,存在安全风险)
ftp -n <<< "open ftp.example.com\nuser admin password\nget file.txt\nbye"
五、注意事项
-
引号控制变量替换:
<< 'EOF':禁用变量替换(原样输出)。<< "EOF":允许变量替换但需转义双引号。
name="World" cat << 'EOF' # 禁用替换 Hello $name EOF # 输出:Hello $name -
缩进限制:
<<-仅支持制表符(Tab)缩进,空格无效。- 结束标记前的缩进必须一致。
-
性能影响:
- Here Document 在内存中生成临时文件,大文本可能影响性能,建议用文件重定向替代。
六、总结
<<:适合需要多行输入且保留原始格式的场景(如生成配置文件)。<<-:适合脚本中需要缩进的多行代码块,提升可读性。<<<:适合快速传递单行字符串,简化管道操作。
通过合理选择操作符,可以显著提升 Shell 脚本的灵活性和可维护性。
在 Linux Shell 中,<<、<<- 和 <<< 均是输入重定向操作符,但用途和行为有本质区别。以下是对它们的详细对比:
1. <<(标准 Here Document)
- 功能:将多行文本块作为命令的输入,直到遇到指定的结束标记。
- 语法:
command << DELIMITER 多行内容... DELIMITER - 关键规则:
- 严格匹配:结束标记必须独占一行,且前后不能有空格。
- 缩进问题:内容中允许缩进,但结束标记本身不能被缩进(否则报错)。
- 示例:
cat << EOF This is indented text. But the delimiter "EOF" must be unindented. EOF
2. <<-(缩进 Here Document)
- 功能:与
<<类似,但允许结束标记前包含行首制表符(Tab),从而支持代码格式化。 - 语法:
command <<- DELIMITER 多行内容(可缩进,但只能用 Tab)... DELIMITER # 此处必须用 Tab 缩进,不能用空格 - 关键规则:
- 仅支持 Tab:结束标记前的缩进必须是 Tab,空格会报错。
- 自动去除行首 Tab:内容中的行首 Tab 会被自动删除(但其他位置的缩进保留)。
- 示例:
实际输出:cat <<- EOF This line starts with a Tab. EOF # 这一行用 Tab 缩进This line starts with a Tab.
3. <<<(Here String)
- 功能:将单个字符串作为命令的输入,而非多行文本块。
- 语法:
command <<< "字符串" - 关键规则:
- 单行输入:直接传递引号内的内容(含换行需手动指定,如
\n)。 - 变量解析:默认启用变量替换(类似双引号)。
- 单行输入:直接传递引号内的内容(含换行需手动指定,如
- 示例:
grep "foo" <<< "foo bar baz" # 搜索 "foo",直接匹配成功
对比表格
| 操作符 | 输入类型 | 缩进支持 | 结束标记要求 | 典型场景 |
|---|---|---|---|---|
<< | 多行文本块 | ❌ | 严格独占一行 | 生成配置文件、长文本输入 |
<<- | 多行文本块 | ✔️(仅 Tab) | 允许行首 Tab 缩进 | 需代码美观的脚本内多行输入 |
<<< | 单行字符串 | ❌ | 无结束标记 | 快速传递短内容、变量值 |
使用建议
- 多行格式化文本:
- 优先用
<<-+ Tab 缩进,增强脚本可读性。
- 优先用
- 单行内容:
- 用
<<<简化输入,替代echo "内容" | command。
- 用
- 严格兼容性:
<<-是 Bash 扩展语法,若需跨 Shell 兼容,用<<。
常见错误
<<-中使用空格:cat <<- EOF Text... EOF # 错误!缩进必须是 Tab,不能是空格<<<中的多行内容:cat <<< "Line 1 Line 2" # 实际输出是单行 "Line 1\nLine 2"
掌握这些操作符的差异,可大幅提升脚本编写的效率和可维护性!