web-dev-qa-db-ja.com

コマンドをエコーし​​てから実行しますか? (メーカーのように)

Bashを一種の冗長モードにする方法はありますか?たとえば、シェルスクリプトを実行しているときに、実行する前に実行するコマンドをエコーアウトしますか?つまり、make?の出力と同様に、実行されたコマンド(およびその出力)を確認できるようにします。

つまり、次のようなシェルスクリプトを実行している場合

echo "Hello, World"

次の出力が欲しい

echo "Hello, World"
Hello, World

または、コマンドを出力して実行するecho_and_runというbash関数を作成することはできますか?

$ echo_and_run echo "Hello, World"
echo "Hello, World"
Hello, World
17
mjs

Bashのprintf%q形式指定子と組み合わせて使用​​して、スペースが保持されるように引数をエスケープすることができます。

function echo_and_run {
  echo "$" "$@"
  eval $(printf '%q ' "$@") < /dev/tty
}
7
mjs

echoを呼び出す前に、evalコマンドに対して独自の関数を作成できます。

Bashにはデバッグ機能もあります。一度あなたはset -x bashは、実行する前に各コマンドを表示します。

cnicutar@Shell:~/dir$ set -x
cnicutar@Shell:~/dir$ ls
+ ls --color=auto
a  b  c  d  e  f
31
cnicutar

あなたの質問の2番目の部分に答えるために、これがあなたが望むことをするシェル関数です:

echo_and_run() { echo "$*" ; "$@" ; }

私はこれに似たものを使用します:

echo_and_run() { echo "\$ $*" ; "$@" ; }

これは、コマンドの前に$を出力します(シェルプロンプトのように見え、コマンドであることを明確にします)。実行中のコマンドの一部(すべてではない)を表示したい場合に、スクリプトでこれを使用することがあります。

他の人が述べているように、引用符は失われます。

$ echo_and_run echo "Hello, world"
$ echo Hello, world
Hello, world
$ 

しかし、それを回避する良い方法はないと思います。シェルは、echo_and_runが引用符を表示する前に引用符を取り除きます。スペースやその他のシェルメタ文字を含む引数をチェックし、必要に応じて引用符を追加するスクリプトを作成できます(実際に入力した引用符と必ずしも一致するとは限りません)。

17
Keith Thompson

スクリプトまたは対話型セッションでbashコマンドラインに追加するか、setコマンドを介して追加できる2つの便利なシェルオプション:

  • -vシェルの入力行を読み取ったときに印刷します。
  • -x各単純コマンド、forコマンド、caseコマンド、selectコマンド、または算術forコマンドを展開した後、表示します。 PS4の展開された値の後に、コマンドとその展開された引数または関連するWordリストが続きます。
1
Toby Speight

追加のタイムスタンプとI/O情報については、Debiandevscriptsパッケージのannotate-outputコマンドを検討してください。

annotate-output echo hello

出力:

13:19:08 I: Started echo hello
13:19:08 O: hello
13:19:08 I: Finished with exitcode 0

ここで、が存在しないファイルを探し、[〜#〜] stderr [のE:に注意してください。 〜#〜]出力:

annotate-output ls nosuchfile

出力:

13:19:48 I: Started ls nosuchfile
13:19:48 E: ls: cannot access 'nosuchfile': No such file or directory
13:19:48 I: Finished with exitcode 2
0
agc

他の人の実装に追加するために、これは引数の解析を含む私の基本的なスクリプトの定型文です(これは冗長レベルを切り替える場合に重要です)。

#!/bin/sh

# Control verbosity
VERBOSE=0

# For use in usage() and in log messages
SCRIPT_NAME="$(basename $0)"

ARGS=()

# Usage function: tells the user what's up, then exits.  ALWAYS implement this.
# Optionally, prints an error message
# usage [{errorLevel} {message...}
function usage() {
    local RET=0
    if [ $# -gt 0 ]; then
        RET=$1; shift;
    fi
    if [ $# -gt 0 ]; then
        log "[$SCRIPT_NAME] ${@}"
    fi
    log "Describe this script"
    log "Usage: $SCRIPT_NAME [-v|-q]" # List further options here
    log "   -v|--verbose    Be more verbose"
    log "   -q|--quiet      Be less verbose"
    exit $RET
}

# Write a message to stderr
# log {message...}
function log() {
    echo "${@}" >&2
}

# Write an informative message with decoration
# info {message...}
function info() {
    if [ $VERBOSE -gt 0 ]; then
        log "[$SCRIPT_NAME] ${@}"
    fi
}

# Write an warning message with decoration
# warn {message...}
function warn() {
    if [ $VERBOSE -gt 0 ]; then
        log "[$SCRIPT_NAME] Warning: ${@}"
    fi
}

# Write an error and exit
# error {errorLevel} {message...}
function error() {
    local LEVEL=$1; shift
    if [ $VERBOSE -gt -1 ]; then
        log "[$SCRIPT_NAME] Error: ${@}"
    fi
    exit $LEVEL
}

# Write out a command and run it
# vexec {minVerbosity} {prefixMessage} {command...}
function vexec() {
    local LEVEL=$1; shift
    local MSG="$1"; shift
    if [ $VERBOSE -ge $LEVEL ]; then
        echo -n "$MSG: "
        local CMD=( )
        for i in "${@}"; do
            # Replace argument's spaces with ''; if different, quote the string
            if [ "$i" != "${i/ /}" ]; then
                CMD=( ${CMD[@]} "'${i}'" )
            else
                CMD=( ${CMD[@]} $i )
            fi
        done
        echo "${CMD[@]}"
    fi
    ${@}
}

# Loop over arguments; we'll be shifting the list as we go,
# so we keep going until $1 is empty
while [ -n "$1" ]; do
    # Capture and shift the argument.
    ARG="$1"
    shift
    case "$ARG" in
        # User requested help; sometimes they do this at the end of a command
        # while they're building it.  By capturing and exiting, we avoid doing
        # work before it's intended.
        -h|-\?|-help|--help)
            usage 0
            ;;
        # Make the script more verbose
        -v|--verbose)
            VERBOSE=$((VERBOSE + 1))
            ;;
        # Make the script quieter
        -q|--quiet)
            VERBOSE=$((VERBOSE - 1))
            ;;
        # All arguments that follow are non-flags
        # This should be in all of your scripts, to more easily support filenames
        # that start with hyphens.  Break will bail from the `for` loop above.
        --)
            break
            ;;
        # Something that looks like a flag, but is not; report an error and die
        -?*)
            usage 1 "Unknown option: '$ARG'" >&2
            ;;
        #
        # All other arguments are added to the ARGS array.
        *)
            ARGS=(${ARGS[@]} "$ARG")
            ;;
    esac
done
# If the above script found a '--' argument, there will still be items in $*;
# move them into ARGS
while [ -n "$1" ]; do
    ARGS=(${ARGS[@]} "$1")
    shift
done

# Main script goes here.

後で...

vexec 1 "Building myapp.c" \
    gcc -c myapp.c -o build/myapp.o ${CFLAGS}

注:これは、パイプされたコマンドには適用されません。これらの種類のものをbash-cするか、中間変数またはファイルに分割する必要があります。

0
Fordi