Jak mogę warunkowo przekazać podpowłokę przez „czas”?


9

Mam skrypt instalacyjny dla pudełka Vagrant, w którym mierzyłem pojedyncze kroki time. Teraz chciałbym warunkowo włączyć lub wyłączyć pomiary czasu.

Na przykład wcześniej linia wyglądałaby tak:

time (apt-get update > /tmp/last.log 2>&1)

Teraz pomyślałem, że mogę po prostu zrobić coś takiego:

MEASURE_TIME=true
[[ $MEASURE_TIME = true ]] && TIME="time --format=%e" || TIME=""

$TIME (apt-get update > /tmp/last.log 2>&1)

Ale to nie zadziała:

syntax error near unexpected token `apt-get'
`$TIME (apt-get update > /tmp/last.log 2>&1)'

W czym problem?


3
Musisz tylko osiągnąć docelową prędkość, aby kondensator topnika zadziałał;)
złotowłosa

2
@Goldilocks Masz na myśli magnes ? ;)
CVn

Odpowiedzi:


13

Aby móc określić czas podpowłoki, potrzebujesz time słowa kluczowego , a nie polecenia.

timeSłów kluczowych, częścią języka, jest uznawane tylko jako takie, gdy wszedł dosłownie i jako pierwsze słowo polecenia (aw przypadku ksh93, a następny znak nie startuje z -). Nawet wejście "time"nie zadziała, nie mówiąc już o tym $TIME(a timezamiast tego zostanie odebrane jako wywołanie polecenia).

Możesz użyć tutaj aliasów, które są rozwinięte przed wykonaniem kolejnej rundy analizowania (aby powłoka rozpoznała to timesłowo kluczowe):

shopt -s expand_aliases
alias time_or_not=
TIMEFORMAT=%E

MEASURE_TIME=true
[[ $MEASURE_TIME = true ]] && alias time_or_not=time

time_or_not (apt-get update > /tmp/last.log 2>&1)

Słowo time kluczowe nie przyjmuje opcji (z wyjątkiem -pin bash), ale format można ustawić za pomocą TIMEFORMATzmiennej inbash . ( shoptczęść jest również bashspecyficzna, inne powłoki na ogół tego nie potrzebują).


Powiedziałeś „Aby mieć czas na podpowłokę, potrzebujesz słowa kluczowego time”. Zastanawiam się, czy to zachowanie udokumentowało gdzieś?
cuonglm,

@cuonglm, patrzinfo -f bash --index-search=time
Stéphane Chazelas,

3

Chociaż jest aliasto jeden ze sposobów, można to zrobić evalrównież za pomocą - po prostu nie tyle chcesz evalwykonania polecenia, ile chceszeval deklaracji polecenia .

podoba mi się alias es - używam ich cały czas, ale bardziej lubię funkcje - szczególnie ich zdolność do obsługi parametrów i że niekoniecznie muszą być one rozszerzane w pozycji poleceń, jak jest to wymagane dla aliases.

Pomyślałem więc, że może też chcesz spróbować:

_time() if   set -- "${IFS+IFS=\$2;}" "$IFS" "$@" && IFS='
';      then set -- "$1" "$2" "$*"; unset IFS
             eval "$1 $TIME ${3#"$1"?"$2"?}"
        fi

Chodzi $IFSgłównie o to $*. Ważne jest, aby wynik ( subshell bit )był również wynikiem rozszerzenia powłoki, a więc w celu rozwinięcia argumentów w parsowalny ciąg, którego używam "$*" (nie eval "$@", nawiasem mówiąc, chyba że jesteś pewien, że wszystkie argumenty można połączyć spacjami) . Rozdzielony separator między argumentami "$*"jest pierwszym bajtem wejściowym $IFS, więc kontynuowanie pracy bez pewności co do jej wartości może być niebezpieczne. Tak więc funkcja zapisuje $IFS, ustawia ją na \newline wystarczająco długo, aby set ... "$*"w "$3", unsets, a następnie resetuje swoją wartość, jeśli wcześniej miała taką.

Oto małe demo:

TIME='set -x; time'
_time \( 'echo "$(echo any number of subshells)"' \
         'command -V time'                        \
         'hash time'                              \
      \) 'set +x'

Widzisz, umieściłem w poleceniu dwie wartości $TIME- dowolna liczba jest w porządku - nawet żadna - ale upewnij się, że została poprawnie zmieniona i zacytowana - i to samo dotyczy argumentów _time(). Wszystkie zostaną połączone w jeden ciąg komend, gdy zostaną wykonane - ale każdy argument otrzymuje własny \newline, dzięki czemu można je stosunkowo łatwo rozłożyć. W przeciwnym razie możesz zebrać je wszystkie w jednym, jeśli chcesz, i rozdzielić je na \newline lub średniki lub co tam masz. Po prostu upewnij się, że pojedynczy argument reprezentuje polecenie, które możesz swobodnie umieścić w swoim wierszu w skrypcie podczas jego wywoływania.\(, na przykład jest w porządku, o ile w końcu zostanie zastosowane \). Zasadniczo normalne rzeczy.

Po evalotrzymaniu powyższego fragmentu wygląda on następująco:

+ eval 'IFS=$2;set -x; time (
echo "$(echo any number of subshells)"
command -V time
hash time
)
set +x'

A jego wyniki wyglądają jak ...

WYNIK

+++ echo any number of subshells
++ echo 'any number of subshells'
any number of subshells
++ command -V time
time is a shell keyword
++ hash time
bash: hash: time: not found

real    0m0.003s
user    0m0.000s
sys     0m0.000s
++ set +x

hashBłąd wskazuje, że nie masz /usr/bin/timezainstalowanego (bo nie) i commandniech nas nie wie czym jest uruchomiony. Trailing set +xjest kolejną komendą wykonywaną po time (co jest możliwe) - ważne jest, aby zachować ostrożność przy komendach wejściowych podczas evalpisania czegokolwiek.


Czy jest jakiś powód, aby tego nie robić _time() { eval "$TIME $@"; }? Korzystanie z funkcji ma tę wadę, że wprowadza inny zakres dla zmiennych (nie jest to problem dla podpowłoki)
Stéphane Chazelas

@ StéphaneChazelas - ze względu na cytaty w "$@"rozszerzeniu. Lubię jednego argumentu eval- w przeciwnym razie robi się strasznie. Ummm .... co masz na myśli mówiąc o innym zakresie? Myślałem, że to tylko functionfunkcje ...
Mikeserv

evaldołącza do argumentów przed wykonaniem, co próbujesz zrobić w bardzo zawiły sposób. Miałem na myśli, _time 'local var=1; blah'że uczyni to varlokalnym lokalnym _timelub _time 'echo "$#"'wydrukuje $#_timefunkcję, a nie wywołującego.
Stéphane Chazelas,

@ StéphaneChazelas - Mam tutaj dwie kompletki zakładek. Zgadza się - evalkonkluduje swoje argumenty na przestrzeniach - co, jak mówisz, jest dokładnie tym, co tutaj robię - chociaż nie było to początkowe zamierzenie. Naprawię to. evaling "$*"w przeciwieństwie do "$@"to nawyk Podniosłam po wielu run-in jest ze command not foundkiedy args zostały połączone w niewłaściwym miejscu. W każdym razie, chociaż argumenty są ostatecznie wykonywane w funkcji, myślę, że wystarczy je rozwinąć przy wywołaniu. Tak czy "$#"inaczej bym zrobił .
mikeserv

+1 za fajny pokręcony hackery ...
Stéphane Chazelas
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.