Echo命令,然后运行它? (喜欢制作)

问题描述 投票:16回答:7

有没有办法让bash进入一种冗长的模式,这样,当它运行一个shell脚本时,它会在运行它之前回显它将要运行的命令?也就是说,可以看到运行的命令(以及它们的输出),类似于make的输出?

也就是说,如果运行shell脚本就好

echo "Hello, World"

我想要以下输出

echo "Hello, World"
Hello, World

或者,是否可以编写一个名为echo_and_run的bash函数,它将输出一个命令然后运行它?

$ echo_and_run echo "Hello, World"
echo "Hello, World"
Hello, World
bash shell makefile echo
7个回答
31
投票

在调用echo之前,您可以为eval命令创建自己的函数。

Bash还有一个调试功能。一旦你set -x bash将执行它之前显示每个命令。

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

17
投票

要回答你问题的第二部分,这里有一个shell函数可以做你想要的:

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

我使用类似的东西:

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

它在命令前打印$(它看起来像一个shell提示符,并且更清楚它是一个命令)。当我想要显示它正在执行的一些(但不是全部)命令时,我有时会在脚本中使用它。

正如其他人提到的,它确实失去了引号:

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

但我认为没有任何好办法可以避免这种情况;在echo_and_run有机会看到它们之前,shell剥离了引号。您可以编写一个脚本来检查包含空格和其他shell元字符的参数,并根据需要添加引号(它仍然不一定与您实际键入的引号相匹配)。


7
投票

可以将bash的printf%q格式说明符结合使用来转义参数,以便保留空格:

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

0
投票

要添加到其他人的实现,这是我的基本脚本样板,包括参数解析(如果您要切换详细级别,这很重要)。

#!/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
投票

两个有用的shell选项可以添加到bash命令行或通过脚本或交互式会话中的set命令:

  • -v在读取时打印shell输入行。
  • -x扩展每个简单命令后,for命令,case命令,select命令或算术for命令,显示PS4的扩展值,后跟命令及其扩展参数或关联的单词列表。

0
投票

有关额外的时间戳和I / O信息,请考虑Debian的devscripts包中的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

现在查找一个不存在的文件,并注意E:for STDERR输出:

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

-1
投票

使用下面提到的简单shell脚本创建名为“echo_and_run”的可执行(+ x)​​基本脚本!

#!/bin/bash
echo "$*"
$@

$ ./echo_and_run“echo Hello,World”

echo Hello, World
Hello, World

或者只是使用一个衬垫bash功能。

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

甚至这种变体也可以起作用:

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

然而,cnicutar对set -x的态度是可靠的,强烈建议。

© www.soinside.com 2019 - 2024. All rights reserved.