実行時にbashスクリプトから特定のコマンドのみを印刷するにはどうすればよいですか?


19

呼び出すときに渡すコマンドライン引数に基づいたさまざまなifステートメントを含むbashスクリプトがあります。どのコマンドが実行されているかについて何らかの出力があると、これらすべてのifステートメントの流れを確認するのに役立ちますが、現在の解決策では情報が多すぎます。

set -vスクリプトで使用すると、コマンドがスクリプトで実行されたときに画面に印刷されるのを見るのに多少役立ちました、コマンドが多すぎます。これは、スクリプトの完全なコピーのようなものです。

実行されているコマンドを示す出力が必要ですが、コメント、改行、ifステートメント内の式などは表示したくありません。

-vオプションで生成されたすべての出力を正規表現に渡してから印刷する方法はありますか?または、特定の「タイプ」のコマンドのみを出力するbashを取得する他のソリューション(たとえば、bash固有のステートメント、コメントなどではなく、実行可能ファイルを使用していますか?)

[1] /programming/257616/sudo-changes-path-whyはこれに非常に役立ち、set -v使用方法の提案を得ました。

編集

私が実行しているものに似た(しかし同一ではない)スクリプト:

#!/bin/bash

#get verbose command output
set -v

env=$1

if [ "$env" == "dev" ]; then
    python ascript.py
fi

if [ "$env" == "prod" ]; then

    #launching in prod will most likely fail if not run as root. Warn user if not running as root.
    if [ $EUID -ne 0 ]; then
        echo "It doesn't look like you're running me as root. This probably won't work. Press any key to continue." > /dev/stderr
        read input
    fi

    #"stop" any existing nginx processes
    pkill -f nginx

    nginx -c `pwd`/conf/artfndr_nginx.conf

fi

このスクリプトから出力ラインの2つの可能なセットだけが必要です。最初:

python ascript.py

二番目:

pkill -f nginx
nginx -c /some/current/directory/conf/artfndr_nginx.conf

1
もちろん、解析することはできますが、スクリプトを見せて、set -v出力のどの部分が必要か、どの部分が必要でないかを説明しない限り、私たちは助けられません。
テルドン

回答:


12

より複雑なbashスクリプトを作成する場合、コマンドを実行するために小さな関数を使用します。コマンドは、実行されたコマンドをログファイルに出力します。

runthis(){
    ## print the command to the logfile
    echo "$@" >> $LOG
    ## run the command and redirect it's error output
    ## to the logfile
    eval "$@" 2>> $LOG 
}

次に、スクリプトで、次のようなコマンドを実行します。

runthis "cp /foo/bar /baz/"

コマンドを出力したくない場合は、通常どおり実行してください。

$LOGファイル名に設定するか、単に削除してstdoutまたはstderrに出力できます。


+1また、「重要な」コマンドに短い名前の関数をv python ascript.py
追加

@Trindazは、コマンドに変数を渡す必要がある場合に引用符があります。変数にスペースが含まれていると、問題が発生する可能性があります。
テルドン

eval ..... || ok=1:「eval ...」が失敗した場合にのみ、okを「1」に設定しますか?たぶんあなたは「&&」を意味していたのでしょうか?そして、それを意味するのであれば、eval行の前に「ok = 0」を追加して、毎回「リセット」します。または、単に「ok」の名前を「error」に変更しますか?それがここで意味されたようです。最後に:eval "$@" 2>> "$LOG" && error=0 || error=1
オリビエデュラック

@OlivierDulac、私が使用しているこのバージョンでは、okコマンドが失敗した場合にスクリプトを停止する変数があります。ここでは関係ないので、削除しましたが、を削除するのを忘れました|| ok=1。おかげで、今修正。
テルドン

優れたソリューション!"コマンドが既に"sで囲まれているため、評価ステートメントの周囲を削除する必要がありました
gromit190

11

サブシェルを使用します。すなわち:

( set -x; cmd1; cmd2 )

例えば:

( set -x; echo "hi there" )

プリント

+ echo 'hi there'
hi there

set -x; cmd; set +xはいくつかの理由でこれを好む。まず、set -x以前にオンになった場合にリセットされません。次に、内部のスクリプトを終了しても、詳細設定がオンの状態でトラップが実行されることはありません。
オリバーゴンジャ

2

@terdonのようなメソッドが使用されているのを見てきました。これは、高レベルのプログラミング言語がロガーと呼ぶものの始まりであり、log4J(Java)、log4Perl(Perl)などの完全なライブラリとして提供されます。

set -x前述のように、Bashで似たようなものを取得できますが、それを使用してコードのブロックをラップすることで、コマンドのサブセットのみをデバッグすることができます。

$ set -x; cmd1; cmd2; set +x

使用できる1ライナーパターンを次に示します。

$ set -x; echo  "hi" ;set +x
+ echo hi
hi
+ set +x

スクリプト内の複数のコマンドに対して、このようにラップできます。

set -x
cmd1
cmd2
set +x

cmd3

Log4Bash

ほとんどの人は気づいていませんが、Bashにはlog4 *もあり、Log4Bashです。より控えめなニーズがある場合、これをセットアップするのに時間をかける価値があるかもしれません。

log4bashは、Bashスクリプトのロギングを改善するための試みです(つまり、Bashのロギングを少なくします)。

log4bashの使用例をいくつか示します。

#!/usr/bin/env bash
source log4bash.sh

log "This is regular log message... log and log_info do the same thing";

log_warning "Luke ... you turned off your targeting computer";
log_info "I have you now!";
log_success "You're all clear kid, now let's blow this thing and go home.";
log_error "One thing's for sure, we're all gonna be a lot thinner.";

# If you have figlet installed -- you'll see some big letters on the screen!
log_captains "What was in the captain's toilet?";

# If you have the "say" command (e.g. on a Mac)
log_speak "Resistance is futile";

Log4sh

もし私がlog4 *フレームワークのフルパワーとして分類するものが欲しいなら、Log4shを試してみます。

抜粋

log4shは、もともと私が働いていた本番環境のいくつかでロギングが多すぎるか、または十分ではなかったロギングの問題を解決するために開発されました。特にCronの仕事は、すべてが機能している、または何も機能しなかったが詳細な理由ではないことを伝える絶え間なく迷惑なメールで最も頭痛がしました。現在、シェルスクリプトからのログ記録が重要である環境でlog4shを使用していますが、単純な「Hello、fix me!」以上のものが必要です。ロギングメッセージのタイプ。あなたが見たものが好きであるか、改善に関する提案があれば、私にメールを送ってください。プロジェクトに十分な関心がある場合は、さらに開発します。

log4shはLinuxのBourne Again Shell(/ bin / bash)で開発されましたが、SolarisのデフォルトのBourne Shell(/ bin / sh)で動作するように細心の注意を払っています。自分で使用するプラットフォーム。

Log4shは、Bashだけでなく、いくつかのシェルをサポートしています。

  • ボーンシェル(sh)
  • BASH-GNU Bourne Again SHell(bash)
  • DASH(ダッシュ)
  • Kornシェル(ksh)
  • pdksh-パブリックドメインKornシェル(pdksh)

また、Linuxだけでなく、いくつかのOSでもテストされています。

  • Cygwin(Windowsの場合)
  • FreeBSD(ユーザーがサポート)
  • Linux(Gentoo、RedHat、Ubuntu)
  • Mac OS X
  • Solaris 8、9、10

log4 *フレームワークを使用するには学習に時間がかかりますが、ロギングの要求がより厳しい場合には価値があります。Log4shは、アペンダーを定義し、表示される出力のフォーマットを制御できる構成ファイルを使用します。

#! /bin/sh
#
# log4sh example: Hello, world
#

# load log4sh (disabling properties file warning) and clear the default
# configuration
LOG4SH_CONFIGURATION='none' . ./log4sh
log4sh_resetConfiguration

# set the global logging level to INFO
logger_setLevel INFO

# add and configure a FileAppender that outputs to STDERR, and activate the
# configuration
logger_addAppender stderr
appender_setType stderr FileAppender
appender_file_setFile stderr STDERR
appender_activateOptions stderr

# say Hello to the world
logger_info 'Hello, world'

今実行すると:

$ ./log4sh.bash 
INFO - Hello, world

注:上記は、コードの一部としてアペンダーを構成します。必要に応じて、独自のファイルlog4sh.propertiesなどに抽出することができます。

詳細が必要な場合は、Log4sh優れたドキュメントを参照してください。


追加されたメモをおset寄せいただきありがとうございますが、私が抱える主な問題は、導入する必要のあるすべてのコマンド、コメントなどを交互に変更することです。そのため、スクリプトの先頭に1文字の関数呼び出しを追加して、スクリプト内のすべての「重要な」行は、今のところ私にとってすっきりしているように見えました。(単一の文字関数は、単一文字の名前を持っているため)
Trindaz

@Trindaz-申し訳ありませんが、まだ回答を終えていません。terdonが提供する機能より多くのニーズがある場合は、log4bashを見てください。
slm

1
@Trindaz-私は時々似たようなことをします。私が使用した他のアプローチechoは、自分の関数をラップすることですmecho。そして、-v物事をオフにしたいときに冗長性と呼ばれるプログラムにスイッチを渡します。関数の名前を指定する2番目の引数スイッチを使用して制御することもできるため、ロギングを制御する2つの軸があります。ただし、これは多くの場合、log4bashを必要とするゲートウェイです。
slm

1
@Trindaz set -xは、実行されるとコマンドを出力します。コメントは出力されません。set -xデバッグには実用的です(set -vこれはあまり有用ではありません)。Zshの出力set -xはbashよりも優れています。たとえば、現在実行されている関数とソース行番号が表示されます。
ジル 'SO-悪であるのをやめる'

ありがとう@Gilles本当だが、それは私にこの場合には行き過ぎた表現の拡張、与えなかったこと
Trindaz

1

あなたは可能性がありtrap DEBUG、その後、テスト変数を。これをスクリプトの先頭に追加します。BASH_COMMAND

log() {
    case "$1" in
        python\ *)
            ;&
        pkill\ *)
            printf "%s\n" "$*"
            ;;
    esac
}

trap 'log "$BASH_COMMAND"' DEBUG

コードは読み取り可能です。最初の引数がpythonor pkillで始まるかどうかをテストし、その場合は出力します。そして、トラップはBASH_COMMAND(実行されるコマンドを含む)最初の引数として使用します。

$ bash foo.sh dev
python ascript.py
python: can't open file 'ascript.py': [Errno 2] No such file or directory
$ bash foo.sh prod
It doesn't look like you're running me as root. This probably won't work. Press any key to continue.

pkill -f nginx
foo.sh: line 32: nginx: command not found

caseglob を使用している間も、同様に簡単に実行できることに注意してください。

if [[ $1 =~ python|nginx ]]
then
    printf "%s" "$*"
fi

そして、正規表現を使用します。


0

これは、Steven Pennyのきちんとした機能の改訂版です。引数をカラーで出力し、必要に応じて引用符で囲みます。これを使用して、トレースするコマンドを選択的にエコーします。引用符が出力されるため、スクリプトをデバッグしている間に、印刷された行をコピーして端末に貼り付けてすぐに再実行できます。最初のコメントを読んで、私が何を変えたのか、そしてその理由を知りましょう。

xc() # $@-args
{
  cecho "$@"
  "$@"
}
cecho() # $@-args
{
  awk '
  BEGIN {
    x = "\047"
    printf "\033[36m"
    while (++i < ARGC) {
      if (! (y = split(ARGV[i], z, x))) {
        printf (x x)
      } else {
        for (j = 1; j <= y; j++) {
          printf "%s", z[j] ~ /[^[:alnum:]%+,./:=@_-]/ ? (x z[j] x) : z[j]
          if (j < y) printf "\\" x
        }
      }
      printf i == ARGC - 1 ? "\033[m\n" : FS
    }
  }
  ' "$@"
}

出力の使用例:

# xc echo "a b" "c'd" "'" '"' "fg" '' " " "" \# this line prints in green

echo 'a b' c\'d \' '"' fg '' ' ' '' '#' this line prints in green

a b c'd ' " fg # this line prints in green

上記の2行目は緑色で印刷され、コピーアンドペーストして3行目を再現できます。

さらなる発言

@ Steven-Pennyのオリジナルのxcは賢く、彼はすべての功績に値します。しかし、いくつかの問題に気づきましたが、彼の投稿に直接コメントすることはできませんでした。評判が十分ではないからです。だから私は彼の投稿に対して提案された編集を行ったが、レビュアーは私の編集を拒否した。したがって、私はこの回答としてコメントを投稿することに頼りましたが、スティーブペニー自身の回答を編集できるようにしたいと思いました。

Steven-Pennyの答えで私が変えたこと

修正:ヌル文字列の印刷-それらは印刷されませんでした。修正:次を含む文字列の印刷%-awk構文エラーが発生しました。交換しfor (j in ...)for (j = 0, ...)前者は、配列トラバーサル(それのawkの実装依存)の順序を保証するものではありませんので。移植性のために8進数に0を追加しました。

更新

Steven Penny'sはその後、彼の答えのこれらの問題を修正したので、これらの発言は私の答えの歴史的記録にのみ留まります。詳細については、コメントのセクションを参照してください。


0

POSIX stdlibライブラリの「sh_trace」シェル関数を使用して、コマンドを実行する前にカラーで印刷できます。例:

プレビュー

基になるAwk関数:

function sh_trace(ary,   b, d, k, q, w, z) {
  b = "\47"
  for (d in ary) {
    k = split(ary[d], q, b)
    q[1]
    if (d - 1)
      z = z " "
    for (w in q) {
      z = z (!k || q[w] ~ "[^[:alnum:]%+,./:=@_-]" ? b q[w] b : q[w]) \
      (w < k ? "\\" b : "")
    }
  }
  printf "\33[36m%s\33[m\n", z
  system(z)
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.