1. ホーム
  2. bash

[解決済み] Bashで最後に実行したコマンドをエコーする?

2023-02-14 10:50:48

質問

bashスクリプトの中で最後に実行されたコマンドをechoしようとしています。私はそれを行うための方法を見つけたいくつかの history,tail,head,sed これは、パーサーの立場から見て、コマンドがスクリプトの特定の行を表している場合にはうまくいきます。しかし、ある状況下では期待された出力が得られません。例えば、コマンドが case 文の中に挿入された場合などです。

スクリプトが

#!/bin/bash
set -o history
date
last=$(echo `history |tail -n2 |head -n1` | sed 's/[0-9]* //')
echo "last command is [$last]"

case "1" in
  "1")
  date
  last=$(echo `history |tail -n2 |head -n1` | sed 's/[0-9]* //')
  echo "last command is [$last]"
  ;;
esac

出力されます。

Tue May 24 12:36:04 CEST 2011
last command is [date]
Tue May 24 12:36:04 CEST 2011
last command is [echo "last command is [$last]"]

[Q] bash スクリプトのどこでどのようにコマンドが呼び出されたかに関係なく、最後に実行されたコマンドをエコーする方法を見つけるのを誰か助けてくれませんか?

私の答え

SO'erの仲間からのありがたい貢献にもかかわらず、私は run 関数 (すべてのパラメーターを 1 つのコマンドとして実行し、失敗したときにコマンドとそのエラー コードを表示する) を書くことを選択しました。

-チェックしたいコマンドの前に run これはそれらを1行にまとめ、スクリプトの簡潔さには影響しません。

-これらのコマンドのいずれかでスクリプトが失敗するたびに、私のスクリプトの最後の出力行は、どのコマンドが失敗したかをその終了コードとともに明確に表示するメッセージになり、デバッグが容易になります。

スクリプトの例です。

#!/bin/bash
die() { echo >&2 -e "\nERROR: $@\n"; exit 1; }
run() { "$@"; code=$?; [ $code -ne 0 ] && die "command [$*] failed with error code $code"; }

case "1" in
  "1")
  run ls /opt
  run ls /wrong-dir
  ;;
esac

出力されます。

$ ./test.sh
apacheds  google  iptables
ls: cannot access /wrong-dir: No such file or directory

ERROR: command [ls /wrong-dir] failed with error code 2

複数の引数、引数としてのbash変数、引用された引数...を持つ様々なコマンドをテストしてみました。 run 関数はそれらを壊しませんでした。私がこれまでに見つけた唯一の問題は、壊れるエコーを実行することですが、私はとにかく私のエコーをチェックするつもりはありません。

どのように解決するのですか?

コマンド履歴は、対話型の機能です。履歴に入力されるのは、完全なコマンドのみです。例えば case という構文は、シェルが解析し終わった時点で全体として入力されます。履歴を検索する際に history による履歴の検索も、シェル展開 ( !:p )) は、あなたが望んでいると思われること、つまり単純なコマンドの呼び出しを表示することを行います。

DEBUG トラップ を使うと、単純なコマンドの実行の直前にコマンドを実行することができます。実行するコマンドの文字列版(単語はスペースで区切られています)は BASH_COMMAND 変数に格納されます。

trap 'previous_command=$this_command; this_command=$BASH_COMMAND' DEBUG
…
echo "last command is $previous_command"

なお previous_command はコマンドを実行するたびに変化するので、それを利用するために変数に保存しておく。前のコマンドの戻り値も知りたい場合は、両方を1つのコマンドに保存してください。

cmd=$previous_command ret=$?
if [ $ret -ne 0 ]; then echo "$cmd failed with error code $ret"; fi

さらに、コマンドに失敗したときだけ中断したい場合は set -e を使って、最初の失敗したコマンドでスクリプトを終了させることができます。最後のコマンドを表示するには EXIT トラップ .

set -e
trap 'echo "exit $? due to $previous_command"' EXIT

もし、スクリプトが何をしているのか追跡しようとするならば、 このようなことは忘れて、次のように使ってください。 set -x .