やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Shellリダイレクト・パイプ全集

 全集は無理かもだがパターン網羅したい。

成果物

目次

  1. はじめに
  2. リダイレクト
    1. ヒアドキュメント
  3. パイプ
    1. 名前付きパイプ

はじめに

fd path named path 和名
0 /dev/fd/0 /dev/stdin 標準入力
1 /dev/fd/1 /dev/stdout 標準出力
2 /dev/fd/2 /dev/stderr 標準エラー出力

リダイレクト

意味 コード
StdOutのみ echo StdOut
StdOutのみ echo StdOut 1>&1
StdOutのみ echo StdOut >&1
StdErrのみ echo StdErr 1>&2
StdErrのみ echo StdErr >&2
StdOutを消す echo StdOut > /dev/null
StdErrを消す { echo StdErr >&2; } 2>/dev/null
StdOut,StdErr両方 { echo StdOut >&1; echo StdErr >&2; }
StdOut,StdErrの内StdErrのみ { echo StdOut >&1; echo StdErr >&2; } > /dev/null
StdOut,StdErrの内StdOutのみ { echo StdOut >&1; echo StdErr >&2; } 2> /dev/null
StdOut,StdErr両方消す { echo StdOut >&1; echo StdErr >&2; } 1>/dev/null 2>/dev/null
意味 コード
ファイルに上書き(set -Cでエラー) echo StdOut > a.txt
ファイルに上書き echo StdOut >| a.txt
ファイルに追記 echo StdOut >> a.txt
StdOut,StdErrを別ファイルに上書き { echo StdOut >&1; echo StdErr >&2; } 1> 1.txt 2> 2.txt
StdOut,StdErrを同ファイルに上書き { echo StdOut >&1; echo StdErr >&2; } &> a.txt
意味 コード
プロセス置換(入力) diff <(echo -e 'A\nB') <(echo -e 'A\nZ')
プロセス置換(出力) exec 2> >(tee -a err.log >&2)
意味 コード
指定fdで入出力する { exec 3< <(seq 1 5); cat <&3; exec 3>&-; }
空きfdで入出力する { exec {fd}< <(seq 1 5); cat <&${fd}; exec {fd}>&-; }
指定fdで一行ずつ入出力する { exec 3< <(echo -e 'A\nB\nC'); while read line <&3; do echo ${line}1; done; exec 3>&-; }
空きfdで一行ずつ入出力する { exec {fd}< <(echo -e 'A\nB\nC'); while read line <&${fd}; do echo ${line}1; done; exec {fd}>&-; }
意味 コード
両方向リダイレクト cat <(echo 'A') > a.txt
意味 コード
ファイル入力 cat < a.txt
ヒアドキュメント cat << EOS ... EOS
ヒアストリング cat <<< "redirect."

ヒアドキュメント

cat << EOS
A
B
C
EOS

 変数展開できる。ただし~/home/{user}に展開してくれない。$(echo ~)とすれば展開される。

cat << EOS
~
${HOME}
$(echo AAA)
$((1+1))
EOS

 展開を抑制するには'EOS'とする。

cat << 'EOS'
~
${HOME}
$(echo AAA)
$((1+1))
EOS

 行頭TAB除去(スペースはそのまま除去せず)。

cat <<- EOS
  A
  B
  C
 EOS

 変数への代入・参照。

VAR=$(cat << EOS
A
B
EOS
)
echo "$VAR"

 while文とreadコマンドの併用で一行ずつ処理する。

while read line; do echo ${line}1; done << EOS
A
B
C
EOS

 計算結果だけを出したいなら以下。

{ SUM=0; while read line; do SUM=$((SUM+line)); done; echo $SUM; } << EOS
1
2
3
EOS

パイプ

 パイプ(パイプライン)は前コマンドのstdoutを次コマンドのstdinとする。パイプ間のコマンドはそれぞれ別プロセスである。

  • seq 1 5 | grep '3'

 パイプはしばしばテキストデータ処理を行う。これをフィルタと呼ぶ。フィルタコマンドとその使用例は以下。

echo -e 'A\nB\nC' | cat
echo -e 'A\nB\nC' | grep 'B'
echo -e 'A\nB\nC' | tr '\n' ' '
echo -e 'A\tB\tC' | expand
echo -e 'A\tB\tC' | expand | unexpand -a
echo -e 'B\nA\nA' | sort | uniq > distinct.txt
echo -e '2\t3\n1\t2' | tsort
echo -e 'A\nB\nC' | sed -e 's/^B$/Z/g'
echo -e 'A\nB\nC' | nl
echo -e 'A\nB\nC' | wc -l
echo -e 'A\nB\nC' | head -n 1
echo -e 'A\nB\nC' | tail -n 1
echo -e 'A\nB\nC' | sed -n 2p
echo -e 'A\nB\nC' | sed -n 2,3p
echo -e 'A\tB\tC' | cut -f2
echo -e 'A\nB\nC' | awk '{ printf("%s%s\n", $1, "1"); }'
echo {{a..z},{A..Z}} | tr -d ' ' | fold -w 26

 プロセス置換を使ったフィルタコマンドの例は以下。

diff <(echo -e 'A\nB') <(echo -e 'A\nZ')
comm <(echo -e 'A\nB') <(echo -e 'A\nZ')
paste <(echo -e '1\n2\n3') <(echo -e 'A\nB')
join -a 1 <(echo -e '1\n2\n3') <(echo -e '1\tA\n3\tC')
iconv -f UTF-8 -t SJIS <(echo 'あいうえお')

 他のコマンドはUNIXユーティリティの一覧を参照。

 以下|&はstdoutだけでなくstderrも次コマンドのstdinに与える。

{ echo StdOut; echo StdErr >&2; } |& nl

 ふつうのパイプ|でやるとstderrはパイプ先に渡されず端末に出力されることがわかる。

{ echo StdOut; echo StdErr >&2; } | nl

 stderrを捨てたいなら以下。

{ echo StdOut; echo StdErr >&2; } 2>/dev/null | nl

名前付きパイプ

  • mkfifo p0; cat < p0 & echo A > p0; rm p0;

 一文ずつ解説すると以下。

コード 概要
mkfifo p0 名前付きパイプp0作成(実体はファイル)
cat < p0 & 名前付きパイプをcatへ入力(ブロックされるので&で非同期実行)
echo A > p0 名前付きパイプにAを書き込む(ブロック終了)
rm p0 名前付きパイプp0を削除する

 名前付きパイプにアクセスするとブロックされて待受になるのが特徴。使い道があまりない。

対象環境

  • Raspbierry pi 3 Model B+
  • Raspbian stretch 9.0 2018-11-13
  • bash 4.4.12
$ uname -a
Linux raspberrypi 4.14.98-v7+ #1200 SMP Tue Feb 12 20:27:48 GMT 2019 armv7l GNU/Linux