最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Bash重定向完全指南(超詳細(xì)!)

 更新時(shí)間:2026年01月04日 11:51:44   作者:cxxu1375  
Linux命令重定向是一種強(qiáng)大的功能,它允許你將命令的輸出(標(biāo)準(zhǔn)輸出或標(biāo)準(zhǔn)錯(cuò)誤)重定向到文件或其他設(shè)備,從而靈活地控制命令的輸出和錯(cuò)誤信息的處理方式,這篇文章主要介紹了Bash重定向完全指南的相關(guān)資料,需要的朋友可以參考下

一、重定向概述

重定向是 Shell 編程中最核心的概念之一。它允許你改變命令的輸入來源和輸出目的地,而不是使用默認(rèn)的鍵盤輸入和屏幕輸出。

1.1 文件描述符基礎(chǔ)

在 Unix/Linux 系統(tǒng)中,每個(gè)進(jìn)程啟動(dòng)時(shí)都會(huì)自動(dòng)打開三個(gè)標(biāo)準(zhǔn)文件描述符:

文件描述符名稱默認(rèn)設(shè)備用途
0stdin(標(biāo)準(zhǔn)輸入)鍵盤程序讀取輸入
1stdout(標(biāo)準(zhǔn)輸出)終端屏幕程序正常輸出
2stderr(標(biāo)準(zhǔn)錯(cuò)誤)終端屏幕程序錯(cuò)誤信息
# 查看當(dāng)前 shell 打開的文件描述符
ls -la /proc/$$/fd
# 輸出示例:
# lrwx------ 1 user user 64 Jan  3 10:00 0 -> /dev/pts/0
# lrwx------ 1 user user 64 Jan  3 10:00 1 -> /dev/pts/0
# lrwx------ 1 user user 64 Jan  3 10:00 2 -> /dev/pts/0

# cxxu@CxxuDesk 17:14:09> <~>
$ ls -la /proc/$$/fd
total 0
dr-x------ 2 cxxu cxxu  6 Jan  3 16:57 .
dr-xr-xr-x 9 cxxu cxxu  0 Jan  3 16:57 ..
lrwx------ 1 cxxu cxxu 64 Jan  3 16:57 0 -> /dev/pts/0
lrwx------ 1 cxxu cxxu 64 Jan  3 16:57 1 -> /dev/pts/0
l-wx------ 1 cxxu cxxu 64 Jan  3 16:57 10 -> /home/cxxu/output.txt
lrwx------ 1 cxxu cxxu 64 Jan  3 16:57 2 -> /dev/pts/0
lrwx------ 1 cxxu cxxu 64 Jan  3 16:57 255 -> /dev/pts/0
lrwx------ 1 cxxu cxxu 64 Jan  3 16:57 5 -> /dev/ptmx

其中$$表示當(dāng)前shell進(jìn)程id

1.2 重定向的基本原理

重定向操作符會(huì)在命令執(zhí)行之前Shell 解釋和處理。這意味著:

# 即使命令不存在,文件也會(huì)被創(chuàng)建(因?yàn)橹囟ㄏ蛳扔诿顖?zhí)行)
nonexistent_command > output.txt
ls -la output.txt
# -rw-r--r-- 1 user user 0 Jan  3 10:00 output.txt  # 文件已創(chuàng)建,但為空

此外,shell是從左往右出來重定向的,這在包含多個(gè)重定向用法的命令行要注意.

1.3 重定向操作符的位置

重定向可以出現(xiàn)在命令的任何位置:

# 以下三種寫法完全等價(jià)
echo hello > file.txt
> file.txt echo hello
echo > file.txt hello

shell優(yōu)先識(shí)別(處理)命令行中的重定向部分>file.txt,剩下的是命令部分(非重定向部分),即echo hello

然后文件file.txt被創(chuàng)建(如果沒有的話),然后echo命令的輸出被重定向到file.txt

1.4 重定向的處理順序

重定向按照從左到右的順序處理,這一點(diǎn)極其重要:

# 示例 1:stdout 和 stderr 都重定向到 dirlist
ls > dirlist 2>&1
# 執(zhí)行順序:
# 1. > dirlist     : fd 1 指向 dirlist 文件
# 2. 2>&1          : fd 2 復(fù)制 fd 1(此時(shí) fd 1 已指向 dirlist)
# 結(jié)果:fd 1 和 fd 2 都指向 dirlist

# 示例 2:只有 stdout 重定向到 dirlist
ls 2>&1 > dirlist
# 執(zhí)行順序:
# 1. 2>&1          : fd 2 復(fù)制 fd 1(此時(shí) fd 1 還指向終端)
# 2. > dirlist     : fd 1 指向 dirlist 文件
# 結(jié)果:fd 2 指向終端,fd 1 指向 dirlist

二、輸入重定向(Input Redirection)

2.1 基本語法

[n]<word
  1. 如果省略 n,默認(rèn)為文件描述符 0(標(biāo)準(zhǔn)輸入)。
  2. 并且n不一定是整數(shù)(雖然常見的情況是整數(shù)),還可以是單詞{varname},這是高級(jí)用法.

2.2 判斷是shell還是外部程序打開文件

# 從文件讀取輸入(shell打開文件,cat接受輸入)
cat < input.txt

# 等價(jià)于下面(但語義不同:一個(gè)是 shell 打開文件,一個(gè)是 cat 打開文件)
# cat直接打開文件
cat input.txt

# 區(qū)別演示,打開不存在的文件時(shí),上述兩種寫法報(bào)錯(cuò)者不同(分別由shell程序和cat程序拋出)
cat < nonexistent.txt  # shell 報(bào)錯(cuò):bash: nonexistent.txt: No such file or directory
cat nonexistent.txt    # cat 報(bào)錯(cuò):cat: nonexistent.txt: No such file or directory

2.3 實(shí)際應(yīng)用

# 使用 while 循環(huán)逐行讀取文件
while read -r line; do
    echo "Line: $line"
done < data.txt

# 從文件讀取數(shù)據(jù)進(jìn)行排序(shell從左往右解釋重定向,首先將sort命令的輸入重定向?yàn)閡nsorted.txt,然后將sort處理結(jié)果重定向到sorted.txt)
sort < unsorted.txt > sorted.txt

# 用于交互式程序的自動(dòng)化
mysql -u root -p < setup.sql

# 多個(gè)輸入重定向(后者覆蓋前者)
cat < file1.txt < file2.txt  # 只會(huì)讀取 file2.txt

2.4 指定文件描述符

# 將文件描述符 3 關(guān)聯(lián)到輸入文件
exec 3< input.txt
read line <&3      # 從 fd 3 讀取一行(注意指針偏移,下一次讀取自動(dòng)讀取原文中的第2行,依次類推)
echo "$line"
exec 3<&-          # 關(guān)閉 fd 3

具體案例:

# cxxu@CxxuDesk 17:46:08> <~>
$ exec 3<config.txt

# cxxu@CxxuDesk 17:46:25> <~>
$ read l1 <&3

# cxxu@CxxuDesk 17:46:48> <~>
$ echo "$l1"
line1

# cxxu@CxxuDesk 17:46:57> <~>
$ read l2 <&3

# cxxu@CxxuDesk 17:47:07> <~>
$ echo "$l2"
line2

檢查指定文件描述符是否被關(guān)閉

# cxxu@CxxuDesk 17:48:14> <~>
$ ls -la /proc/$$/fd
total 0
dr-x------ 2 cxxu cxxu  8 Jan  3 16:57 .
dr-xr-xr-x 9 cxxu cxxu  0 Jan  3 16:57 ..
lrwx------ 1 cxxu cxxu 64 Jan  3 16:57 0 -> /dev/pts/0
lrwx------ 1 cxxu cxxu 64 Jan  3 16:57 1 -> /dev/pts/0
l-wx------ 1 cxxu cxxu 64 Jan  3 16:57 10 -> /home/cxxu/output.txt
l-wx------ 1 cxxu cxxu 64 Jan  3 17:34 11 -> /home/cxxu/output.txt
lrwx------ 1 cxxu cxxu 64 Jan  3 16:57 2 -> /dev/pts/0
lrwx------ 1 cxxu cxxu 64 Jan  3 16:57 255 -> /dev/pts/0
lr-x------ 1 cxxu cxxu 64 Jan  3 17:46 3 -> /home/cxxu/config.txt
lrwx------ 1 cxxu cxxu 64 Jan  3 16:57 5 -> /dev/ptmx

# cxxu@CxxuDesk 17:48:30> <~>
$ exec 3<&-

# cxxu@CxxuDesk 17:48:43> <~>
$ ls -la /proc/$$/fd
total 0
dr-x------ 2 cxxu cxxu  7 Jan  3 16:57 .
dr-xr-xr-x 9 cxxu cxxu  0 Jan  3 16:57 ..
lrwx------ 1 cxxu cxxu 64 Jan  3 16:57 0 -> /dev/pts/0
lrwx------ 1 cxxu cxxu 64 Jan  3 16:57 1 -> /dev/pts/0
l-wx------ 1 cxxu cxxu 64 Jan  3 16:57 10 -> /home/cxxu/output.txt
l-wx------ 1 cxxu cxxu 64 Jan  3 17:34 11 -> /home/cxxu/output.txt
lrwx------ 1 cxxu cxxu 64 Jan  3 16:57 2 -> /dev/pts/0
lrwx------ 1 cxxu cxxu 64 Jan  3 16:57 255 -> /dev/pts/0
lrwx------ 1 cxxu cxxu 64 Jan  3 16:57 5 -> /dev/ptmx

三、輸出重定向(Output Redirection)

3.1 基本語法

[n]>[|]word

如果省略 n,默認(rèn)為文件描述符 1(標(biāo)準(zhǔn)輸出)。

其中|啟用的時(shí)候(變成>|)會(huì)強(qiáng)制重定向,即便shell選項(xiàng)設(shè)置為默認(rèn)不覆蓋(noclobber)

3.2 基本行為

# 創(chuàng)建新文件或覆蓋現(xiàn)有文件
echo "Hello" > output.txt

# 如果文件存在,內(nèi)容被清空后寫入
echo "World" > output.txt
cat output.txt
# World  (注意:Hello 已被覆蓋)

3.3 noclobber 選項(xiàng)和>|

noclobber 選項(xiàng)可以防止意外覆蓋文件:

# 啟用 noclobber
set -o noclobber
# 或者
set -C

# 嘗試覆蓋現(xiàn)有文件會(huì)失敗
echo "test" > existing_file.txt
# bash: existing_file.txt: cannot overwrite existing file

# 使用 >| 強(qiáng)制覆蓋
echo "test" >| existing_file.txt  # 成功

# 禁用 noclobber
set +o noclobber
# 或者
set +C

3.4 重定向錯(cuò)誤輸出

# 只重定向標(biāo)準(zhǔn)錯(cuò)誤
ls nonexistent 2> error.log

# 標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤分別重定向
command > stdout.log 2> stderr.log

# 丟棄錯(cuò)誤信息
command 2> /dev/null

# 丟棄所有輸出
command > /dev/null 2>&1

四、追加重定向(Appending Redirected Output)

4.1 基本語法

[n]>>word

4.2 使用示例

# 追加內(nèi)容到文件
echo "First line" > log.txt
echo "Second line" >> log.txt
echo "Third line" >> log.txt
cat log.txt
# First line
# Second line
# Third line

# 追加錯(cuò)誤輸出
command 2>> error.log


# 實(shí)際應(yīng)用:日志記錄(演示目錄/var/log/可能會(huì)遇到權(quán)限問題)
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $*" >> ~/myapp.log
}
log "Application started"
log "Processing data..."

五、同時(shí)重定向標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤(輸出聚合)

5.1 兩種語法形式(&>,>&)

# 形式 1(推薦)
&>word

# 形式 2
>&word

當(dāng)使用第二種形式時(shí),word 不可能擴(kuò)展為一個(gè)數(shù)字或‘ - ’。

兩者在語義上等同于:

>word 2>&1

5.2 使用示例

# 所有輸出都寫入文件
command &> all_output.txt

# 丟棄所有輸出
command &> /dev/null

# 等價(jià)寫法對(duì)比
ls /exists /nonexistent &> output.txt
ls /exists /nonexistent > output.txt 2>&1  # 等價(jià)

5.3 追加形式&>>

# 追加所有輸出
command &>> all_output.txt

# 等價(jià)于
command >> all_output.txt 2>&1

相比于> word 2>&1只在開頭更改為>>

5.4 注意事項(xiàng)

當(dāng)使用 >&word 這個(gè)形式時(shí),word 不能是數(shù)字- 因?yàn)槟菚?huì)被解釋為文件描述符操作

# 這是復(fù)制文件描述符,而不是將輸出聚合到名為3的文件
command >&3            # fd 1 復(fù)制到 fd 3,相當(dāng)于1>&3

# 安全起見,推薦使用 &> 形式
command &>output.txt   # 清晰明確
# 
ls /usr/bin/env 'abab' &> 3
# cxxu@CxxuDesk 18:12:30> <~>
$ ls /usr/bin/env 'abab' &> 3

# cxxu@CxxuDesk 18:15:07> <~>
$ cat 3
ls: cannot access 'abab': No such file or directory
/usr/bin/env

六、Here Documents

6.1 基本語法

[n]<<[-]word
    here-document
delimiter

6.2 基本用法

# 基本的 here document
cat << EOF
This is line 1
This is line 2
This is line 3
EOF

# 輸出:
# This is line 1
# This is line 2
# This is line 3

6.3 變量展開行為

name="World"

# 不帶引號(hào)的分隔符:變量會(huì)被展開
cat << EOF
Hello, $name!
Current directory: $(pwd)
Sum: $((1 + 2))
EOF
# 輸出:
# Hello, World!
# Current directory: /home/user
# Sum: 3

# 帶引號(hào)的分隔符:禁止所有展開
cat << 'EOF'
Hello, $name!
Current directory: $(pwd)
Sum: $((1 + 2))
EOF
# 輸出:
# Hello, $name!
# Current directory: $(pwd)
# Sum: $((1 + 2))

# 部分引用也會(huì)禁止展開
cat << "EOF"
Hello, $name!
EOF
# 輸出:
# Hello, $name!

cat << E"O"F
Hello, $name!
EOF
# 輸出:
# Hello, $name!

6.4 <<- 去除前導(dǎo)制表符

# 使用 <<- 可以在腳本中保持良好的縮進(jìn)
if true; then
    cat <<- EOF
		This line has a tab prefix
		So does this one
		And this one too
	EOF
fi
# 輸出(前導(dǎo)制表符被去除):
# This line has a tab prefix
# So does this one
# And this one too

# 注意:只去除制表符(Tab),不去除空格

6.5 實(shí)際應(yīng)用

# 生成配置文件
cat << EOF > /etc/myapp.conf
# Configuration file for MyApp
# Generated on $(date)

server_name = localhost
port = 8080
debug = false
EOF

# SQL 腳本
mysql -u root -p << EOF
CREATE DATABASE IF NOT EXISTS mydb;
USE mydb;
CREATE TABLE IF NOT EXISTS users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100)
);
EOF

# 多行 SSH 命令
ssh user@remote << 'EOF'
cd /var/log
tail -n 100 syslog
df -h
EOF

# 函數(shù)中使用
generate_html() {
    cat << EOF
<!DOCTYPE html>
<html>
<head><title>$1</title></head>
<body>
<h1>$1</h1>
<p>$2</p>
</body>
</html>
EOF
}
generate_html "Welcome" "Hello, World!" > index.html

七、Here Strings

7.1 基本語法

[n]<<< word

7.2 使用示例

# 基本用法
cat <<< "Hello, World!"
# Hello, World!

# 與 here document 對(duì)比
# Here document(多行)
cat << EOF
Hello
EOF

# Here string(單行,更簡(jiǎn)潔)
cat <<< "Hello"

# 變量展開
name="Alice"
cat <<< "Hello, $name!"
# Hello, Alice!

# 命令替換
cat <<< "Today is $(date +%A)"
# Today is Friday

# 用于需要 stdin 輸入的命令
read var <<< "input value"
echo "$var"
# input value

# 實(shí)際應(yīng)用:處理字符串
# 計(jì)算字符串中的單詞數(shù)
wc -w <<< "one two three four"
# 4

# 字符串分割
IFS=: read user pass uid gid gecos home shell <<< "root:x:0:0:root:/root:/bin/bash"
echo "User: $user, Home: $home, Shell: $shell"
# User: root, Home: /root, Shell: /bin/bash

# bc 計(jì)算器
bc <<< "scale=2; 10/3"
# 3.33

7.3 Here String vs echo + 管道

# 使用 here string(更高效,不創(chuàng)建子進(jìn)程)
cat <<< "Hello"

# 使用 echo + 管道(創(chuàng)建子進(jìn)程)
echo "Hello" | cat

# 性能差異示例
time for i in {1..10000}; do cat <<< "test" > /dev/null; done
time for i in {1..10000}; do echo "test" | cat > /dev/null; done
# here string 通常更快

八、復(fù)制文件描述符

8.1 復(fù)制輸入文件描述符

[n]<&word
# 將 fd 3 設(shè)為 fd 0 的副本
exec 3<&0

# 實(shí)際應(yīng)用:保存和恢復(fù) stdin
exec 3<&0           # 保存原始 stdin 到 fd 3
exec 0< input.txt   # 重定向 stdin 到文件
# ... 一些操作 ...
exec 0<&3           # 從 fd 3 恢復(fù) stdin
exec 3<&-           # 關(guān)閉 fd 3

8.2 復(fù)制輸出文件描述符

[n]>&word
# 將 fd 3 設(shè)為 fd 1 的副本
exec 3>&1

# 經(jīng)典模式:交換 stdout 和 stderr
exec 3>&1 1>&2 2>&3 3>&-
# 執(zhí)行后:原來的 stdout 變成 stderr,原來的 stderr 變成 stdout

8.3 關(guān)閉文件描述符

# 關(guān)閉輸入文件描述符
exec 3<&-

# 關(guān)閉輸出文件描述符
exec 3>&-

# 關(guān)閉標(biāo)準(zhǔn)輸入
exec 0<&-

# 關(guān)閉標(biāo)準(zhǔn)輸出
exec 1>&-

8.4 實(shí)際應(yīng)用示例

# 示例:同時(shí)捕獲 stdout 和 stderr 到不同變量
{
    output=$(command 2>&1 1>&3)
    exit_code=$?
} 3>&1
error=$output
# 此時(shí) $output 包含 stderr,stdout 正常顯示

# 更完整的版本
capture_output() {
    local stdout stderr exit_code
    exec 3>&1 4>&2
    stdout=$( { stderr=$( "$@" 2>&1 1>&3 3>&- ); exit_code=$?; } 2>&1 )
    exec 3>&- 4>&-
    echo "stdout: $stdout"
    echo "stderr: $stderr"
    echo "exit: $exit_code"
}

九、移動(dòng)文件描述符

9.1 移動(dòng)輸入文件描述符

[n]<&digit-

移動(dòng) = 復(fù)制 + 關(guān)閉原描述符

# 將 fd 3 移動(dòng)到 fd 0
exec 0<&3-
# 等價(jià)于:
# exec 0<&3
# exec 3<&-

9.2 移動(dòng)輸出文件描述符

[n]>&digit-
# 將 fd 3 移動(dòng)到 fd 1
exec 1>&3-

# 實(shí)際應(yīng)用:日志重定向后恢復(fù)
exec 3>&1                    # 保存 stdout
exec 1> logfile.txt          # stdout 重定向到文件
echo "This goes to log"
exec 1>&3-                   # 恢復(fù) stdout 并關(guān)閉 fd 3(一步完成)
echo "This goes to terminal"

9.3 復(fù)制 vs 移動(dòng) 對(duì)比

# 復(fù)制:原文件描述符保持打開
exec 3>&1      # fd 3 是 fd 1 的副本,fd 1 仍然有效

# 移動(dòng):原文件描述符被關(guān)閉
exec 3>&1-     # fd 3 是 fd 1 的副本,fd 1 被關(guān)閉

十、讀寫文件描述符

10.1 基本語法

[n]<>word

以讀寫模式打開文件,如果文件不存在則創(chuàng)建。

10.2 使用示例

# 以讀寫模式打開文件
exec 3<> data.txt

# 讀取內(nèi)容
read line <&3
echo "Read: $line"

# 寫入內(nèi)容(注意:會(huì)覆蓋當(dāng)前位置的內(nèi)容)
echo "New content" >&3

# 關(guān)閉
exec 3>&-

10.3 實(shí)際應(yīng)用

# 簡(jiǎn)單的文件鎖實(shí)現(xiàn)
lockfile="/tmp/mylock"
exec 200<>$lockfile
flock -n 200 || { echo "Another instance running"; exit 1; }
# ... 執(zhí)行需要鎖保護(hù)的操作 ...

# 修改文件的特定部分(需要配合 seek,通常用其他工具更方便)
# Bash 本身不支持 seek,這種用法有限

十一、{varname} 語法詳解

11.1 自動(dòng)分配文件描述符

Bash 4.1+ 支持使用 {varname} 讓 shell 自動(dòng)分配一個(gè) ≥10 的文件描述符:

# 傳統(tǒng)方式:手動(dòng)指定 fd 編號(hào)
exec 3> output.txt

# 新方式:自動(dòng)分配
exec {myfd}> output.txt
echo "Allocated fd: $myfd"    # 輸出類似:Allocated fd: 10

# 使用分配的 fd
echo "Hello" >&$myfd

# 關(guān)閉
exec {myfd}>&-

11.2 無需 exec 的持久文件描述符

這是 {varname} 最強(qiáng)大的特性:

# 在普通命令中打開 fd,且 fd 會(huì)持續(xù)存在
echo "First line" {fd}> output.txt

# fd 在命令結(jié)束后仍然有效
echo "Second line" >&$fd
echo "Third line" >&$fd

cat output.txt
# First line
# Second line
# Third line

# 手動(dòng)關(guān)閉
exec {fd}>&-

11.3 與 exec 方式的對(duì)比

# 方式 1:exec + 固定編號(hào)
exec 3> file.txt
echo "data" >&3
exec 3>&-
# 缺點(diǎn):可能與其他代碼沖突

# 方式 2:exec + {varname}
exec {fd}> file.txt
echo "data" >&$fd
exec {fd}>&-
# 優(yōu)點(diǎn):自動(dòng)分配,不沖突
# 缺點(diǎn):仍需 exec

# 方式 3:命令 + {varname}(最靈活)
: {fd}> file.txt     # : 是空命令
echo "data" >&$fd
exec {fd}>&-
# 優(yōu)點(diǎn):無需 exec,自動(dòng)分配

11.4 varredir_close 選項(xiàng)

# 查看當(dāng)前設(shè)置
shopt varredir_close

# 啟用:當(dāng)變量離開作用域時(shí)自動(dòng)關(guān)閉 fd
shopt -s varredir_close

# 禁用(默認(rèn))
shopt -u varredir_close

11.5 實(shí)際應(yīng)用示例

# 日志系統(tǒng)
init_logging() {
    : {LOG_FD}>> /var/log/myapp.log
}

log() {
    echo "$(date): $*" >&$LOG_FD
}

close_logging() {
    exec {LOG_FD}>&-
}

# 使用
init_logging
log "Application started"
log "Processing..."
close_logging

# 多文件處理
process_files() {
    : {input_fd}< input.txt
    : {output_fd}> output.txt
    : {error_fd}>> errors.log
    
    while read -u $input_fd line; do
        if process "$line"; then
            echo "$line" >&$output_fd
        else
            echo "Failed: $line" >&$error_fd
        fi
    done
    
    exec {input_fd}<&- {output_fd}>&- {error_fd}>&-
}

十二、特殊文件名處理

12.1 /dev/fd/n

# 復(fù)制文件描述符
echo "Hello" > /dev/fd/1    # 等同于 echo "Hello"(寫入 stdout)

# 實(shí)際應(yīng)用:讓不支持 fd 的程序使用管道
diff <(sort file1) <(sort file2)
# 內(nèi)部使用類似 /dev/fd/63 的機(jī)制

12.2 /dev/stdin, /dev/stdout, /dev/stderr

# 明確指定標(biāo)準(zhǔn)流
cat /dev/stdin              # 從標(biāo)準(zhǔn)輸入讀取
echo "Hello" > /dev/stdout  # 寫入標(biāo)準(zhǔn)輸出
echo "Error" > /dev/stderr  # 寫入標(biāo)準(zhǔn)錯(cuò)誤

# 在腳本中恢復(fù)標(biāo)準(zhǔn)流
some_function() {
    # 即使 stdout 被重定向,仍可寫入終端
    echo "Debug info" > /dev/stderr
}

12.3 /dev/tcp 和 /dev/udp

# TCP 連接
exec 3<>/dev/tcp/www.example.com/80
echo -e "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: close\r\n\r\n" >&3
cat <&3
exec 3>&-

# 檢查端口是否開放
timeout 1 bash -c 'cat < /dev/tcp/localhost/22' && echo "SSH port open"

# 簡(jiǎn)單的 HTTP 請(qǐng)求函數(shù)
http_get() {
    local host=$1
    local path=${2:-/}
    exec 3<>/dev/tcp/$host/80
    echo -e "GET $path HTTP/1.1\r\nHost: $host\r\nConnection: close\r\n\r\n" >&3
    cat <&3
    exec 3>&-
}
http_get "example.com" "/index.html"

# UDP 示例(發(fā)送數(shù)據(jù))
echo "test" > /dev/udp/localhost/514   # 發(fā)送到本地 syslog

# 注意:這些是 Bash 特有功能,不是 POSIX 標(biāo)準(zhǔn)
# 某些系統(tǒng)可能需要編譯時(shí)啟用此功能

十三、重定向中的展開

13.1 支持的展開類型

重定向操作符后面的 word 會(huì)經(jīng)歷以下展開:

# 1. 花括號(hào)展開(注意:通常不用于重定向)
# echo test > {a,b}.txt  # 這會(huì)導(dǎo)致錯(cuò)誤,因?yàn)檎归_后有多個(gè)單詞

# 2. 波浪號(hào)展開
echo "test" > ~/output.txt           # 展開為 /home/user/output.txt
echo "test" > ~other_user/file.txt   # 展開為 /home/other_user/file.txt

# 3. 參數(shù)和變量展開
logfile="/var/log/app.log"
echo "test" > "$logfile"

# 4. 命令替換
echo "test" > "$(date +%Y%m%d).log"  # 例如:20260103.log

# 5. 算術(shù)展開
n=1
echo "test" > "file$((n+1)).txt"     # file2.txt

# 6. 引號(hào)去除
echo "test" > "output.txt"           # 引號(hào)被去除

# 7. 文件名展開(通配符)
# 注意:如果展開后得到多個(gè)文件,會(huì)報(bào)錯(cuò)
echo "test" > *.txt                  # 如果匹配多個(gè)文件,報(bào)錯(cuò)
echo "test" > file?.txt              # 如果只匹配一個(gè)文件,OK

# 8. 單詞分割
filename="my file.txt"
echo "test" > $filename              # 錯(cuò)誤!分割成兩個(gè)單詞
echo "test" > "$filename"            # 正確,保持為一個(gè)單詞

13.2 多單詞錯(cuò)誤

# 如果展開結(jié)果是多個(gè)單詞,Bash 會(huì)報(bào)錯(cuò)
files="a.txt b.txt"
echo "test" > $files
# bash: $files: ambiguous redirect

# 解決方案:確保只有一個(gè)單詞
echo "test" > "$files"    # 寫入名為 "a.txt b.txt" 的文件
# 或者使用循環(huán)
for f in $files; do echo "test" > "$f"; done

十四、文件描述符使用注意事項(xiàng)

14.1 避免與 shell 內(nèi)部 fd 沖突

# Shell 內(nèi)部可能使用 fd 10 及以上
# 手動(dòng)使用大編號(hào) fd 時(shí)要小心

# 不推薦
exec 10> myfile.txt    # 可能與 shell 內(nèi)部沖突

# 推薦:使用 {varname} 語法
exec {fd}> myfile.txt  # 讓 shell 分配安全的 fd 編號(hào)

14.2 fd 泄漏

# 錯(cuò)誤:打開 fd 后忘記關(guān)閉
for i in {1..1000}; do
    exec {fd}> "/tmp/file$i.txt"
    echo "data" >&$fd
    # 忘記關(guān)閉 fd!
done
# 可能導(dǎo)致 "Too many open files" 錯(cuò)誤

# 正確:總是關(guān)閉 fd
for i in {1..1000}; do
    exec {fd}> "/tmp/file$i.txt"
    echo "data" >&$fd
    exec {fd}>&-  # 關(guān)閉
done

14.3 子進(jìn)程繼承

# 文件描述符默認(rèn)被子進(jìn)程繼承
exec 3> shared.txt
(
    echo "From subshell" >&3  # 子 shell 可以使用 fd 3
)

# 阻止繼承(使用 close-on-exec 標(biāo)志)
# Bash 本身不直接支持,需要其他手段

十五、綜合實(shí)戰(zhàn)示例

15.1 日志系統(tǒng)

#!/bin/bash

# 初始化日志
LOG_DIR="/var/log/myapp"
mkdir -p "$LOG_DIR"

# 打開日志文件描述符
exec {LOG_INFO}>> "$LOG_DIR/info.log"
exec {LOG_ERROR}>> "$LOG_DIR/error.log"
exec {LOG_DEBUG}>> "$LOG_DIR/debug.log"

# 日志函數(shù)
log_info()  { echo "$(date '+%F %T') [INFO]  $*" >&$LOG_INFO; }
log_error() { echo "$(date '+%F %T') [ERROR] $*" >&$LOG_ERROR; }
log_debug() { echo "$(date '+%F %T') [DEBUG] $*" >&$LOG_DEBUG; }

# 清理函數(shù)
cleanup() {
    exec {LOG_INFO}>&- {LOG_ERROR}>&- {LOG_DEBUG}>&-
}
trap cleanup EXIT

# 使用
log_info "Application started"
log_debug "Initializing components..."

if ! some_operation; then
    log_error "Operation failed"
fi

log_info "Application finished"

15.2 進(jìn)度和輸出分離

#!/bin/bash

# 保存原始 stdout 和 stderr
exec {ORIG_STDOUT}>&1
exec {ORIG_STDERR}>&2

# 重定向所有輸出到日志
exec 1>> process.log 2>&1

# 進(jìn)度信息寫入原始終端
progress() {
    echo "$*" >&$ORIG_STDOUT
}

# 正常輸出寫入日志
echo "Starting process..."

for i in {1..10}; do
    echo "Processing step $i"      # 寫入日志
    progress "Progress: ${i}0%"    # 顯示在終端
    sleep 1
done

echo "Process complete"
progress "Done!"

# 恢復(fù)
exec 1>&$ORIG_STDOUT 2>&$ORIG_STDERR
exec {ORIG_STDOUT}>&- {ORIG_STDERR}>&-

15.3 安全的臨時(shí)文件處理

#!/bin/bash

# 創(chuàng)建臨時(shí)文件并打開 fd(文件可以立即刪除,fd 仍然有效)
tmpfile=$(mktemp)
exec {tmp_fd}<>"$tmpfile"
rm "$tmpfile"  # 刪除文件,但 fd 仍然可用

# 寫入臨時(shí)數(shù)據(jù)
echo "Temporary data line 1" >&$tmp_fd
echo "Temporary data line 2" >&$tmp_fd

# 回到文件開頭讀取
exec {tmp_fd}<&-
exec {tmp_fd}< /dev/fd/$tmp_fd  # 不能直接 seek,需要其他方式

# 更實(shí)用的方式:使用進(jìn)程替換
data=$(cat << 'EOF'
line 1
line 2
line 3
EOF
)

while read line; do
    echo "Processing: $line"
done <<< "$data"

15.4 同時(shí)捕獲 stdout 和 stderr

#!/bin/bash

# 方法:使用臨時(shí)文件和 fd
capture_both() {
    local cmd="$*"
    local stdout_file stderr_file
    
    stdout_file=$(mktemp)
    stderr_file=$(mktemp)
    
    eval "$cmd" > "$stdout_file" 2> "$stderr_file"
    local exit_code=$?
    
    CAPTURED_STDOUT=$(cat "$stdout_file")
    CAPTURED_STDERR=$(cat "$stderr_file")
    
    rm "$stdout_file" "$stderr_file"
    
    return $exit_code
}

# 使用
capture_both ls /exists /nonexistent

echo "Exit code: $?"
echo "Stdout: $CAPTURED_STDOUT"
echo "Stderr: $CAPTURED_STDERR"

十六、常見問題與陷阱

16.1 在管道中的變量作用域

# 問題:管道中的循環(huán)在子 shell 中運(yùn)行
count=0
cat file.txt | while read line; do
    ((count++))
done
echo "$count"  # 輸出 0!變量修改在子 shell 中丟失

# 解決方案 1:使用進(jìn)程替換
count=0
while read line; do
    ((count++))
done < <(cat file.txt)
echo "$count"  # 正確的計(jì)數(shù)

# 解決方案 2:使用 here string
count=0
while read line; do
    ((count++))
done <<< "$(cat file.txt)"
echo "$count"  # 正確的計(jì)數(shù)

# 解決方案 3:使用 lastpipe 選項(xiàng)(Bash 4.2+)
shopt -s lastpipe
count=0
cat file.txt | while read line; do
    ((count++))
done
echo "$count"  # 正確的計(jì)數(shù)

16.2 重定向 vs 管道

# 重定向:直接連接文件和 fd
command < input.txt > output.txt

# 管道:連接兩個(gè)進(jìn)程的 stdout 和 stdin
command1 | command2

# 區(qū)別:
# - 重定向不創(chuàng)建額外進(jìn)程
# - 管道兩邊各有一個(gè)進(jìn)程
# - 重定向的文件需要存在(輸入)或可創(chuàng)建(輸出)

16.3 /dev/null 的正確使用

# 丟棄 stdout
command > /dev/null

# 丟棄 stderr
command 2> /dev/null

# 丟棄所有輸出
command > /dev/null 2>&1
command &> /dev/null  # 簡(jiǎn)寫

# 不要這樣做(創(chuàng)建名為 /dev/null 的普通文件的風(fēng)險(xiǎn))
command > /dev/null 2> /dev/null  # 兩次打開,通常 OK,但不必要

總結(jié)

Bash 重定向是一個(gè)功能強(qiáng)大的系統(tǒng),核心要點(diǎn)包括:

  1. 理解文件描述符 0(stdin)、1(stdout)、2(stderr)的概念
  2. 重定向按從左到右的順序處理
  3. > 覆蓋,>> 追加
  4. 2>&1 將 stderr 重定向到 stdout 當(dāng)前指向的位置
  5. {varname} 語法提供了自動(dòng) fd 分配和持久化能力
  6. Here documents 和 here strings 用于內(nèi)聯(lián)輸入
  7. 特殊文件 /dev/tcp/dev/udp 提供網(wǎng)絡(luò)功能
  8. 使用 exec 可以修改當(dāng)前 shell 的 fd

到此這篇關(guān)于Bash重定向完全指南的文章就介紹到這了,更多相關(guān)Bash重定向內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

白山市| 富阳市| 仁布县| 德化县| 休宁县| 海丰县| 潜江市| 资溪县| 黑山县| 和顺县| 涪陵区| 祁连县| 伊吾县| 邳州市| 宾川县| 本溪市| 慈利县| 永春县| 双桥区| 花莲市| 丰县| 五家渠市| 清河县| 万盛区| 定安县| 类乌齐县| 永顺县| 隆林| 柳河县| 临泉县| 镇远县| 靖州| 安塞县| 开江县| 平武县| 高要市| 宜兰市| 石门县| 额济纳旗| 同江市| 静安区|