N
nob@lit-forge
Shell パイプ & リダイレクト 早見表
標準入出力・エラー出力のリダイレクト、パイプによるコマンド連結、ヒアドキュメント・ヒアストリング、プロセス置換(<(cmd))など、Bash / Zsh のリダイレクト機能を一枚で理解できる早見表。
#shell#パイプ#リダイレクト#Bash
ファイルデスクリプタの基本
- fd 0 = 標準入力(
stdin) - fd 1 = 標準出力(
stdout) - fd 2 = 標準エラー出力(
stderr) - プロセスは常に 3 本の入出力チャネルを持っており、リダイレクトで差し替える
基本のリダイレクト
cmd > out.txt # stdout を上書き cmd >> out.txt # stdout を追記 cmd 2> err.txt # stderr のみ別ファイルへ cmd > out.txt 2>&1 # stdout と stderr を同じファイルへ cmd &> out.txt # 同上(bash 拡張) cmd > /dev/null # stdout を捨てる cmd 2>/dev/null # stderr を捨てる cmd &>/dev/null # 両方捨てる cmd < in.txt # stdin をファイルから供給 cmd < in.txt > out.txt # 入力 + 出力
パイプ(コマンド連結)
cmd1 | cmd2 # cmd1 の stdout → cmd2 の stdin cmd1 |& cmd2 # stderr も流す(bash 拡張、= 2>&1 |) cmd1 | cmd2 | cmd3 # 多段パイプ cmd | tee out.txt # 分岐(画面 + ファイル) cmd | tee -a log.txt # 追記で分岐 cmd | tee file1 file2 # 複数ファイルへ分岐
注意: パイプの各要素はサブシェルで動くので、while read 内で代入した変数は外で使えない。解決には shopt -s lastpipe(bash)か、プロセス置換で回避。
ヒアドキュメント・ヒアストリング
cat <<EOF # ヒアドキュメント(展開あり)
hello $USER
path is $PWD
EOF
cat <<'EOF' # '...' で展開を抑制
$HOME は展開されずリテラル表示
EOF
cat <<-EOF # -EOF で行頭タブ除去(ネスト向け)
インデント無視される
EOF
# ヒアストリング(1 行だけ供給)
grep "^root" <<< "$(cat /etc/passwd)"
wc -c <<< "hello" # 6(改行含む)プロセス置換
# <(cmd) は「cmd の出力を読み込むための疑似ファイル」 diff <(ls dir1) <(ls dir2) # 2 ディレクトリの一覧を diff # >(cmd) は「cmd の入力を書き込むための疑似ファイル」 tee >(gzip > out.gz) >(wc -c > size) < in.txt # while read のサブシェル問題の回避 sum=0 while read n; do sum=$((sum+n)); done < <(seq 1 10) echo "$sum" # 55
xargs と複数行処理
ls *.txt | xargs rm # 危険: ファイル名に空白・改行があると壊れる
find . -name "*.txt" -print0 | xargs -0 rm # NUL 区切りで安全
find . -name "*.jpg" -print0 | xargs -0 -n1 -P4 mogrify -resize 50% # 並列 4
# -I {} で位置指定
ls | xargs -I {} cp {} backup/{}
# 対話確認(-p)
find . -name "*.tmp" | xargs -p rmデータ分岐と集約
# tee で分岐して sha256 と wc を並行計算
cat file | tee >(sha256sum > hash.txt) | wc -l > lines.txt
# 2 本の入力をマージ(paste)
paste file1 file2
# 1 行ずつ区切ってベクトル化(awk のトリック)
seq 1 10 | paste -sd, - # "1,2,3,4,5,6,7,8,9,10"
# 複数コマンドをまとめてリダイレクト
{ echo "=== start ==="; date; ls; echo "=== end ==="; } > report.txt
# サブシェル ( ... ) は環境を分離(cd しても元に戻る)
(cd /tmp && ls)
pwd # 元のディレクトリのまま終了コードとエラー伝播
cmd1 && cmd2 # cmd1 成功時のみ cmd2 cmd1 || cmd2 # cmd1 失敗時のみ cmd2(フォールバック) cmd1 ; cmd2 # 成否に関わらず実行 ! cmd # 終了コード反転 # パイプ中の失敗を検知(bash では最後のコマンドの終了コードだけ見える) set -o pipefail # パイプ途中の失敗も捕まえる cmd1 | cmd2 | cmd3 # どれかが失敗したら $? は非ゼロ # スクリプトで安全策 set -euo pipefail # -e 失敗で即終了 / -u 未定義変数 / -o pipefail trap 'echo "error at $LINENO"' ERR
よくある落とし穴
cmd > out.txt 2>&1の順序が重要:2>&1 > out.txtは stderr がまだ端末を指しているので bug るecho -eは非ポータブル:printf "%s\n"を使う方が一貫- パイプ内の変数代入が効かない:
foo | while read x; do BAR=$x; done; echo $BARは空になる。shopt -s lastpipeか、プロセス置換< <(foo)で回避 cat file | grep fooは無駄:grep foo fileで OK(UUOC = Useless Use Of Cat)- 長い行は行継続
\\で折る: 行末の\\の後ろに空白を入れない