Shellリダイレクト・パイプ全集
全集は無理かもだがパターン網羅したい。
成果物
目次
はじめに
- シェルとはOSのAPIをユーザが操作するためのコマンドライン・インタプリタ
- リダイレクトとはシェル構文のひとつで標準ストリームの入出力先を指定すること
- シェルにおいて標準ストリームはファイルディスクリプタ(fd)で示される
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." |
- POSIX非互換(bashism):
&>
,<()
,>()
,|&
,{fd}
,<<<
ヒアドキュメント
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