2012-09-01 14 views
14

C'è un modo per ottenere bash in una sorta di modalità dettagliata dove, in modo tale che, quando sta eseguendo uno script di shell, echeggia il comando che sta per eseguire prima di eseguirlo? Cioè, in modo che sia possibile vedere i comandi che sono stati eseguiti (così come il loro output), simile all'uscita di make?Comando echo, quindi eseguirlo? (Like make)

Cioè, se si esegue uno script di guscio, come

echo "Hello, World" 

desidero il seguente output

echo "Hello, World" 
Hello, World 

In alternativa, è possibile scrivere una funzione bash chiamato echo_and_run che emette un comando e poi eseguirlo?

$ echo_and_run echo "Hello, World" 
echo "Hello, World" 
Hello, World 

risposta

5

E 'possibile usare bash di printf in collaborazione con il formato %q specificatore per sfuggire gli argomenti in modo che gli spazi sono preservati:

function echo_and_run { 
    echo "$" "[email protected]" 
    eval $(printf '%q ' "[email protected]") < /dev/tty 
} 
22

Si potrebbe fare una propria funzione per echo comandi prima di chiamare eval.

Bash ha anche una funzione di debug. Una volta che hai set -x bash visualizzerà ogni comando prima di eseguirlo.

[email protected]:~/dir$ set -x 
[email protected]:~/dir$ ls 
+ ls --color=auto 
a b c d e f 
+1

+1 e anche, se non è ovvio, 'set -x' può anche andare in guscio script e dureranno per tutta la durata dello script. – jedwards

+0

Questo sembra l'approccio migliore e più affidabile. (E funziona anche con il reindirizzamento, di cui ero preparato a non preoccuparmi, anche se è bello averlo.) L'unico problema è che i comandi "set + x" appaiono anche nell'output ... Inoltre ho scoperto questo: [Voglio un registro delle azioni del mio script] (http://mywiki.wooledge.org/BashFAQ/050). – mjs

+3

@mjs: Se si desidera che il comando echo-ing per l'intero runtime del proprio script, è possibile eseguirlo tramite 'bash -x' (che abilita' set -x' implicitamente e non lo emette). –

-1

Creare script di base eseguibile (+ x) denominato "echo_and_run" con uno script di shell semplice di seguito menzionato!

#!/bin/bash 
echo "$1" 
$1 

$ ./echo_and_run "echo Ciao, mondo"

echo Hello, World 
Hello, World 

Tuttavia, approch di cnicutar per set -x è affidabile e fortemente raccomandato.

+0

No, fai "echo" $ @ "; "$ @" ' –

+0

Questo ingoia le virgolette - per l'esempio sopra, ottieni' echo Hello, World' invece di 'echo" Hello, World "'. – mjs

+0

Giusto, se usiamo "$ @", ingoia le virgolette. uno può usare qualcosa come, './echo_and_run 'echo" Ciao, Mondo "' – Mayura

13

Per rispondere alla seconda parte della tua domanda, ecco una funzione di shell che fa quello che si vuole:

echo_and_run() { echo "[email protected]" ; "[email protected]" ; } 

uso qualcosa di simile a questo:

echo_and_run() { echo "\$ [email protected]" ; "[email protected]" ; } 

che stampa $ davanti il comando (sembra un prompt della shell e rende più chiaro che si tratta di un comando). A volte lo uso negli script quando voglio mostrare alcuni (ma non tutti) dei comandi che sta eseguendo.

Come altri hanno già detto, fa perdere virgolette:

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

ma io non credo che ci sia alcun buon modo per evitare che; le virgolette strisce di shell prima di echo_and_run hanno la possibilità di vederle. È possibile scrivere uno script per verificare gli argomenti contenenti spazi e altri metacaratteri della shell e aggiungere virgolette secondo le necessità (che comunque non corrispondono necessariamente alle virgolette effettivamente digitate).

+0

C'è un modo per fare questo gestire più comandi su una singola riga? I.e 'echo_and_run cd .. && ls' esegue entrambi i comandi, ma emette solo' $ cd ..' – MrTheWalrus

+1

@MrTheWalrus: Questo perché solo 'cd ..' è passato come argomento alla funzione 'echo_and_run()'. È come digitare "echo_and_run cd .." seguito da "ls". Il trucco è di nutrirlo con 'echo_and_run' * come comando singolo *:' echo_and_run sh -c 'cd .. && ls''. Sostituisci '/ bin/sh' con'/bin/bash' se hai bisogno di funzioni specifiche di bash (o ksh, o zsh, o ...) –

+0

Grazie. Potrei dire quale fosse il problema, ma non avevo idea di come rendere il trattamento dell'intera linea come un singolo comando. – MrTheWalrus

0

Per aggiungere alle implementazioni degli altri, questo è il mio boilerplate script di base, tra cui argomento parsing (che è importante se si alterna i livelli di verbosità).

#!/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. 

tardi ...

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

Nota: questa non coprirà i comandi di tubature; hai bisogno di bash -c quei tipi di cose, o di suddividerle in variabili o file intermedi.

0

due opzioni utili di shell che possono essere aggiunti alla linea di bash comando o tramite il comando set in uno script o sessione interattiva:

  • -v linee di ingresso delle coperture della stampa quando vengono letti.
  • -x Dopo l'espansione di ogni comando semplice, for comando, case comando, select comando, o aritmetica comando for, esposizione il valore espanso di PS4, seguito dal comando e suoi espansi argomenti o elenco di parole associate.
0

Per timestamp extra e informazioni di I/O, si consideri il comando annotate-output da devscripts pacchetto Debian s':

annotate-output echo hello 

uscita:

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

Ora guardate per un file che non esiste , e notare il E: per STDERR uscita:

annotate-output ls nosuchfile 

uscita:

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 
Problemi correlati