スクリプト内から出力を less にリダイレクトする

スクリプト内から出力を less にリダイレクトする

次のようにスクリプト内からスクリプトの出力をログファイルにリダイレクトできますexec

#!/bin/bash
exec > stdout.log 2>&1
echo hello world

lessファイルの代わりに出力をリダイレクトすることは可能ですか?試してみました

#!/bin/bash
exec > >(less) 2>&1

# output some text
for (( i=1; i <= 500; i++ )); do echo "hello world $i"; done

しかし、これは奇妙な方法で失敗します...プロンプトは表示されませんが、ターミナルに戻ります。

これをスクリプトの先頭に設定したいと思います (引数、tty などに応じて条件付きにできるようにします)。

答え1

スクリプトはless子プロセスを待機する必要があります。そうしないと、スクリプトが子プロセスの前に終了し、less突然フォアグラウンド プロセス グループから外れてしまい、ターミナルからコマンドを読み取ったり、ターミナル設定を復元したりできなくなります。

lessまた、入力の終了を永久に待機することを防ぐために、スクリプトは入力へのパイプを閉じる必要があります。

これらすべてをまとめると次のようになります。

exec > >(less) 2>&1
trap 'exec >&- 2>&-; wait' EXIT
# >&- 2>&- => close stdout and stderr => cause EOF on less' stdin

seq 1 50000
# the rest of your script

しかし、これはあまり良くなく、他のほとんどのシェルに移植できず、文書化されていない (信頼できない) bash の動作に依存しています。スクリプトにwaitが複数ある場合exec > >(...)、 は正常に動作せず、 で開始された他のバックグラウンド プロセスも待機することになります&


より良いアイデアとしては、無限再帰を避けるために環境変数を使用してスクリプト自身を呼び出すことです。

if [ ! "$CALLED_MYSELF" ]; then
    set -o pipefail # supported in bash, but not in all the shells  
    CALLED_MYSELF=1 "$0" "$@" 2>&1 | less
    exit
fi

seq 1 50000
# the rest of your script

答え2

次のように試してください (行なしexec):

for (( i=1; i <= 500; i++ )); do echo "hello world $i"; done | less

(アップデート)

スクリプト全体を含める場合は、スクリプトを次のようにラップします。{ ... }

#!/bin/bash
{
# output some text
for (( i=1; i <= 500; i++)) do echo "hello world $i"; done

# whatever output you want...

} | less

関連情報