2012-09-01 8 views
14

Czy jest jakiś sposób, aby uzyskać bash w trybie gadatliwym, w którym, gdy działa skrypt powłoki, wypowiada polecenie, które będzie uruchamiane przed uruchomieniem? Oznacza to, że można zobaczyć polecenia, które zostały uruchomione (a także ich wyjście), podobnie jak dane wyjściowe z make?Komenda Echo, a następnie uruchom? (Jak make)

Oznacza to, że jeśli działa skrypt jak

echo "Hello, World" 

chciałbym następujący wynik

echo "Hello, World" 
Hello, World 

Alternatywnie, możliwe jest, aby napisać funkcję bash nazwie echo_and_run że wyświetli polecenie a następnie uruchomić?

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

Odpowiedz

5

Jest możliwe użycie bash na printf w połączeniu ze specyfikacją formatu %q uciec argumenty tak, że przestrzenie są zachowane:

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

Można stworzyć własną funkcję echo poleceń przed wywołaniem eval.

Bash ma również funkcję debugowania. Gdy bę dziesz set -x bash wyś wietli każde polecenie przed jego wykonaniem.

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

+1, a także, jeśli to nie jest oczywiste, 'set -x' można również przejść w skorupach skrypty i będzie trwał na całej długości skryptu. – jedwards

+0

To wydaje się być najlepszym, najbardziej niezawodnym podejściem. (A także działa z przekierowaniem, na które byłem przygotowany, aby się nie przejmować, chociaż miło jest mieć.) Jedyny problem to polecenia 'set + x' pojawiające się również w danych wyjściowych ... Również odkryłem: [Chcę dziennik działań mojego skryptu] (http://mywiki.wooledge.org/BashFAQ/050). – mjs

+3

@mjs: Jeśli chcesz, aby komenda brzmiała dla całego środowiska wykonawczego twojego skryptu, możesz uruchomić go przez 'bash -x' (który włącza' set -x' niejawnie i nie wyprowadza go). –

-1

Utwórz skrypt wykonywalny (+ x) o nazwie "echo_and_run" z poniższym prostym skryptem powłoki!

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

$ ./echo_and_run "echo Hello, World"

echo Hello, World 
Hello, World 

Jednak cnicutar za approch do set -x jest wiarygodna i zdecydowanie zalecane.

+0

Nie, wykonaj "echo" $ @ "; "$ @" ' –

+0

To połyka cytaty - w powyższym przykładzie otrzymasz' echo Hello, World' zamiast 'echo" Hello, World "'. – mjs

+0

Dobrze, jeśli użyjemy "$ @", połyka cytaty. można użyć czegoś takiego, './echo_and_run 'echo" Hello, World "" ' – Mayura

13

Aby odpowiedzieć na drugą część pytania, oto funkcja powłoki, który robi to, co chcesz:

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

Używam coś podobnego do tego:

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

która drukuje $ przed polecenie (wygląda jak znak zachęty powłoki i sprawia, że ​​jest wyraźniejszy, że jest to polecenie). Czasami używam tego w skryptach, gdy chcę pokazać niektóre (ale nie wszystkie) polecenia, które wykonuje.

Jak wspominają inni, to nie stracić cudzysłów:

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

ale nie sądzę, jest dobrym sposobem, aby tego uniknąć; paski cudzysłowów wycinają paski, zanim echo_and_run ma szansę je zobaczyć. Możesz napisać skrypt, który będzie sprawdzał argumenty zawierające spacje i inne metaznaki powłoki i dodawać w miarę potrzeby znaki cudzysłowu (które nadal nie muszą pasować do wprowadzonych znaków cudzysłowu).

+0

Czy istnieje sposób, aby obsłużyć wiele poleceń w jednym wierszu? I tak 'echo_and_run cd .. && ls' uruchamia obydwie komendy, ale wyprowadza tylko' $ cd ..' – MrTheWalrus

+1

@MrTheWalrus: To dlatego, że tylko 'cd ..' jest przekazywane jako argumenty funkcji' echo_and_run() '. To jak pisanie 'echo_and_run cd ..', a następnie' ls'. Sztuką jest przekazanie jej do 'echo_and_run' * jako pojedynczego polecenia *:' echo_and_run sh -c 'cd .. && ls''. Zastąp '/ ​​bin/sh' przez'/bin/bash', jeśli potrzebujesz funkcji specyficznych dla gry (lub ksh, lub zsh, lub ...) –

+0

Dzięki. Mógłbym powiedzieć, na czym polega problem, ale nie miałem pojęcia, jak sprawić, by traktował całą linię jako jedno polecenie. – MrTheWalrus

0

Aby dodać do implementacji innych, to jest mój podstawowy boilerplate skrypt, w tym argumencie parsowanie (co jest ważne, jeśli przełączasz poziomy gadatliwości).

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

Później ...

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

Uwaga: to nie pokryje rurami poleceń; musisz bash -c tego rodzaju rzeczy, lub rozbić je na pośrednie zmienne lub pliki.

0

Dwa przydatnych opcji powłoki, które mogą być dodawane do linii poleceń bash lub poprzez komendę set w skrypcie lub sesji interaktywnej:

  • -v powłoki Drukuj linie wejściowe są one przeczytać.
  • -x Po rozwinięciu każdej prostej komendy, polecenia for, polecenie case, polecenie select lub arytmetyczną polecenie for, wyświetlanie zinterpretowana wartość od PS4, a następnie za pomocą polecenia i jego argumentów lub rozbudowanych związanego listy słów.
0

Dla dodatkowych znaczników czasu i I/O info, pod komendę annotate-output z Debian „s devscripts opakowania:

annotate-output echo hello 

wyjściowa:

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

Spójrzmy teraz dla pliku nie istnieje i zanotuj E: dla STDERR wyjściowa:

annotate-output ls nosuchfile 

wyjściowa:

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