Příprava časového razítka na každý řádek výstupu z příkazu

Chtěl bych přidat časové razítko na každý řádek výstupu z příkazu. Například:

foo bar baz 

by se stalo

[2011-12-13 12:20:38] foo [2011-12-13 12:21:32] bar [2011-12-13 12:22:20] baz 

… v současné době prefixed je čas, kdy byl řádek vytištěn. Jak toho mohu dosáhnout?

Komentáře

Odpověď

moreutils zahrnuje ts, což to dělá celkem pěkně:

command | ts "[%Y-%m-%d %H:%M:%S]"

It eliminuje také potřebu smyčky, na každém řádku výstupu bude uvedeno časové razítko.

$ echo -e "foo\nbar\nbaz" | ts "[%Y-%m-%d %H:%M:%S]" [2011-12-13 22:07:03] foo [2011-12-13 22:07:03] bar [2011-12-13 22:07:03] baz 

Chcete vědět, kdy se server vrátil, že jste restartovali? Stačí spustit ping | ts, problém vyřešen: D.

Komentáře

  • Jak jsem o tom nevěděl ?!?!?! To úžasně doplňuje ocas -f! tail -f /tmp/script.results.txt | ts
  • pokud nemám ‚ příkaz ts, co mám použít?
  • Pokud to ‚ nefunguje, zkuste přesměrovat stderr na stdout, např. ssh -v 127.0.0.1 2>&1 | ts
  • Instalujte provedením sudo apt install moreutils na Debianu a yum install moreutils na Fedora.
  • Myslím, že poukázat na parametr -s je užitečné. Protože to zobrazuje dobu běhu příkazu. Osobně rád používám současně ts i ts -s. Vypadá asi takto: command | ts -s '(%H:%M:%.S)]' | ts '[%Y-%m-%d %H:%M:%S'. Toto předřazuje řádky protokolu takto: [2018-12-04 08:31:00 (00:26:28.267126)] Hai <3

Odpovědět

Za prvé, pokud očekáváte, že tato časová razítka budou skutečně představovat událost, mějte na paměti, že jelikož mnoho programů provádí ukládání linek do vyrovnávací paměti (některé agresivněji než jiné), je důležité si to představit co nejblíže době, kdy by původní řádek byly vytištěny spíše než časové razítko probíhající akce.

Možná budete také chtít zkontrolovat, zda váš příkaz již nemá zabudovanou funkci věnovanou tomu. Příklad: ping -D existuje v některých verzích ping a před každým řádkem vytiskne čas od epochy Unixu. Pokud váš příkaz neobsahuje vlastní metodu, je zde existuje několik metod a nástrojů, které lze použít, mimo jiné:

POSIX shell

Mějte na paměti, že protože mnoho shellů ukládá své řetězce interně jako cstrings, pokud vstup obsahuje null char acter (\0), může to způsobit předčasné ukončení řádku.

command | while IFS= read -r line; do printf "[%s] %s\n" "$(date "+%Y-%m-%d %H:%M:%S")" "$line"; done 

GNU awk

command | gawk "{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0 }" 

Perl

command | perl -pe "use POSIX strftime; print strftime "[%Y-%m-%d %H:%M:%S] ", localtime" 

Python

command | python -c "import sys,time;sys.stdout.write("".join(( " ".join((time.strftime("[%Y-%m-%d %H:%M:%S]", time.localtime()), line)) for line in sys.stdin )))" 

Ruby

command | ruby -pe "print Time.now.strftime("[%Y-%m-%d %H:%M:%S] ")" 

Komentáře

  • Jedním problémem je, že se zapne mnoho programů ještě více vyrovnávací paměti výstupu, když je jejich výstupem místo terminálu kanál.
  • @cjm – True. Některé výstupní vyrovnávací paměti lze zmírnit pomocí stdbuf -o 0, ale pokud program ručně zpracovává své výstupní vyrovnávací paměti, nevyhrál ‚ t nápovědu (pokud existuje možnost deaktivovat / zmenšit velikost výstupní vyrovnávací paměti).
  • U pythonu můžete deaktivovat ukládání do vyrovnávací paměti řádků pomocí python -u
  • @Bwmat No. ... for x in sys.stdin iteruje přes řádky, aniž by je nejprve ukládal do mezipaměti.
  • Udělejte to a dostanete vyrovnávací paměť … pro a v 1 1 1 11; spát 1; echo; hotovo | python -c ‚ import sys, time; sys.stdout.write (“ “ .join ((“ “ .join ((time.strftime (“ [%) Y-% m-% d% H:% M:% S] „, time.gmtime ()), řádek)) pro řádek v sys.stdin))) ‚

odpověď

Pro měření delta po řádku , zkuste gnomon .

Je to nástroj příkazového řádku, trochu jako moreutils „s ts, k doplnění informací o časovém razítku na standardní výstup jiného příkazu. Užitečné pro dlouhotrvající procesy, kde byste chtěli historický záznam toho, co to trvá tak dlouho.

Přenesení všeho na gnomon bude před každý řádek předložit časové razítko, které označuje, jak dlouho byl tento řádek posledním řádkem ve vyrovnávací paměti – tedy jak dlouho trvalo, než se objevil další řádek. Ve výchozím nastavení gnomon zobrazí uplynulé sekundy mezi jednotlivými řádky, ale to je konfigurovatelné.

ukázka gnomonu

Komentáře

  • Při použití živých procesů to vypadá jako skvělá alternativa k ts. Zatímco ts je vhodnější pro neinteraktivní procesy.

Odpovědět

Chtěl bych raději komentovat výše, ale nemohu to „reputačně. Každopádně výše uvedený vzorek Perlu lze bez vyrovnávací paměti zrušit takto:

command | perl -pe "use POSIX strftime; $|=1; select((select(STDERR), $| = 1)[0]); print strftime "[%Y-%m-%d %H:%M:%S] ", localtime" 

The first „$ |“ unbuffers STDOUT. The second one sets stderr as the current default output channel and unbuffes it. Vzhledem k tomu, select vrátí původní nastavení $ |, zabalením select uvnitř select, také resetujeme $ | na výchozí STDOUT.

A ano, můžete je vložit tak, jak jsou. Pro lepší čitelnost jsem to vložil do několika řádků.

A pokud opravdu chcete dosáhnout přesnosti (a máte Time :: Hires nainstalován):

command | perl -pe "use POSIX strftime; use Time::HiRes gettimeofday; $|=1; select((select(STDERR), $| = 1)[0]); ($s,$ms)=gettimeofday(); $ms=substr(q(000000) . $ms,-6); print strftime "[%Y-%m-%d %H:%M:%S.$ms]", localtime($s)" 

Komentáře

  • Funguje to jako kouzlo, aniž byste museli instalovat nestandardní balíčky.

Odpověď

Ryanův příspěvek poskytuje zajímavý nápad, ale v několika ohledech selhává. Při testování pomocí tail -f /var/log/syslog | xargs -L 1 echo $(date +"[%Y-%m-%d %H:%M:%S]") $1 jsem si všiml, že časové razítko zůstane stejné, i když stdout přichází později s rozdílem v sekundových intervalech. Zvažte tento výstup:

[2016-07-14 01:44:25] Jul 14 01:44:32 eagle dhclient[16091]: DHCPREQUEST of 192.168.0.78 on wlan7 to 255.255.255.255 port 67 (xid=0x411b8c21) [2016-07-14 01:44:25] Jul 14 01:44:34 eagle avahi-daemon[740]: Joining mDNS multicast group on interface wlan7.IPv6 with address fe80::d253:49ff:fe3d:53fd. [2016-07-14 01:44:25] Jul 14 01:44:34 eagle avahi-daemon[740]: New relevant interface wlan7.IPv6 for mDNS. 

Moje navrhované řešení je podobné, ale poskytuje správné časové razítko a používá o něco přenosnější printf spíše než echo

| xargs -L 1 bash -c "printf "[%s] %s\n" "$(date +%Y-%m-%d\ %H:%M:%S )" "$*" " bash 

Proč bash -c "..." bash? Protože kvůli možnosti -c nejprve argumentovat nt nebude přiřazen k $0 a nebude se zobrazovat na výstupu. Správný popis -c

Testování tohoto řešení pomocí tail -f /var/log/syslog a (jako pravděpodobně byste mohli hádat) odpojení a opětovné připojení k mé wifi, ukázalo správné časové razítko poskytované jak date, tak i syslog zprávami

Bash může být nahrazen jakýmkoli bourne-like shellem, může být provedeno buď ksh nebo dash, alespoň těmi které mají možnost -c.

Potenciální problémy:

Řešení vyžaduje mít xargs, který je k dispozici na systémech kompatibilních s POSIX, takže většina systémů podobných Unixu by měla být pokryta. Je zřejmé, že nebude fungovat, pokud váš systém není kompatibilní s POSIXem nebo nemá GNU findutils

Odpověď

Většina odpovědí navrhuje použít date, ale je to dost pomalé . Pokud je vaše verze bash větší než 4.2.0, je lepší použít místo toho printf, je to vestavěný bash. Pokud potřebujete podporovat starší verze bash, můžete vytvořit log funkce závisí na verzi bash:

TIMESTAMP_FORMAT="%Y-%m-%dT%H:%M:%S" # Bash version in numbers like 4003046, where 4 is major version, 003 is minor, 046 is subminor. printf -v BV "%d%03d%03d" ${BASH_VERSINFO[0]} ${BASH_VERSINFO[1]} ${BASH_VERSINFO[2]} if ((BV > 4002000)); then log() { ## Fast (builtin) but sec is min sample for most implementations printf "%(${TIMESTAMP_FORMAT})T %5d %s\n" "-1" $$ "$*" # %b convert escapes, %s print as is } else log() { ## Slow (subshell, date) but support nanoseconds and legacy bash versions echo "$(date +"${TIMESTAMP_FORMAT}") $$ $*" } fi 

Zobrazit rychlost rozdíly:

user@host:~$time for i in {1..10000}; do printf "%(${TIMESTAMP_FORMAT})T %s\n" "-1" "Some text" >/dev/null; done real 0m0.410s user 0m0.272s sys 0m0.096s user@host:~$time for i in {1..10000}; do echo "$(date +"${TIMESTAMP_FORMAT}") Some text" >/dev/null; done real 0m27.377s user 0m1.404s sys 0m5.432s 

UPD: místo $(date +"${TIMESTAMP_FORMAT}") je lepší použít $(exec date +"${TIMESTAMP_FORMAT}") nebo dokonce $(exec -c date +"${TIMESTAMP_FORMAT}") příliš rychlé provedení.

UPD2: bash 5 poskytuje EPOCHREALTIME proměnnou pro mikrosekundy granularity, you can use it by this command (about 30% slower than seconds only): printf "%(${TIMESTAMP_FORMAT})T.%s %5d %s\n" ${EPOCHREALTIME/./ } $$ "$*"

Komentáře

  • Tato odpověď je velmi podceňovaná! Obzvláště pěkné je ukázat relativní dopad na výkon.
  • Skvělý návrh, ale bohužel datum neposkytuje subsekundové rozlišení, což je v mém případě požadavek
  • S bash 5.0+ můžete použít tento příkaz: printf "%(${TIMESTAMP_FORMAT})T.%s %5d %s\n" ${EPOCHREALTIME/./ } $$ "$*"

Odpovědět

To lze provést pomocí date a xargs:

... | xargs -L 1 echo `date +"[%Y-%m-%d %H:%M:%S]"` $1 

Vysvětlení:

xargs -L 1 řekne xargs, aby spustil pokračující příkaz pro každý 1 řádek vstupu, a ten prochází v prvním řádku, když to dělá. echo `date +"[%Y-%m-%d %H:%M:%S]"` $1 v zásadě odráží datum se vstupním argumentem na konci

Komentáře

  • Řešení je blízko, ale ‚ t časové razítko správně, pokud jde o výstup oddělené dlouhými časovými obdobími. Také ‚ používáte zpětné lístky a ‚ neuvádíte $1. To ‚ není dobrý styl. Vždy uvádějte proměnné. Kromě toho ‚ používáte echo, které není přenosné. Je ‚ v pořádku, ale v některých systémech nemusí správně fungovat.
  • Po vyzkoušení se zdá, že máte ‚ naprostou pravdu … víte o nějakém způsobu, jak date nechat přehodnotit každý řádek, nebo je to do značné míry beznadějné?

odpověď

nestydatá zástrčka pro něco, co právě jsem napsal k vyřešení tohoto přesného problému: ets , napsáno v Go.

ukázka

Na stránce projektu najdete mnoho příkladů použití.

Pozoruhodný rozdíl oproti stávajícím odpovědím a podobným nabídkám spočívá v tom, že ets je navržen tak, aby za vás spustil váš příkaz v pty (pseudo tty) – tedy simulaci váš příkaz běží nativně v tty. Ve srovnání s výstupem příkazu potrubí do např. ts, díky tomu je časová značka většinou transparentní a řeší spoustu problémů s pipováním:

  • Některé programy se při zápisu do kanálu agresivně vyrovnávají, takže nevidíte žádný výstup a pak spoustu výstupů (jo, můžete je stdbuf, můžete dokonce zabalit stdbuf a ts do aliasu / funkce, ale nebylo by lepší, kdyby věci fungovaly z krabice);
  • Některé programy zakazují při zápisu do kanálu barvu nebo interaktivitu;
  • Stav ukončení je pryč, pokud nezapnete pipefail atd.

Příkazy mohou buďte přímo „ed“, což znamená, že můžete jednoduše přidat ets do svého stávajícího příkazového řádku, nebo to mohou být příkazy prostředí (jak je ukázáno výše v gifu). Samozřejmě, pokud chcete zapojit výstup, ets to může také udělat.

ets podporuje to samé režimy časových značek jako moreutils ts: režim absolutního času, režim uplynulého času a režim přírůstkového času. Používá výchozí nastavení (např. Monotónní hodiny se vždy používají pro uplynulá / přírůstková časová razítka) a má další podporu pro vlastní časová pásma. Zde je podrobné srovnání zde .

Opět https://github.com/zmwangx/ets . Roztočte to, nahlaste chyby atd.

Odpovědět

Umístit toto blízko začátku vašeho skriptu

# prepend a timestamp to all output exec &> >( ts "%Y-%m-%d.%H%M.%.S " ) 

Nebo, pro extra kredit, výstup do souboru protokolu pomocí:

script_name=foobar log_file=$( printf "/tmp/${script_name}-%(%Y-%m-%d)T.%(%H%M%S)T.log" -1 ) echo "note: redirecting output to [${log_file}]" exec &> >( ts "%Y-%m-%d %H:%M:%.S " > ${log_file} ) 

pro výstup do konzoly i do souboru protokolu:

script_name=foobar log_file=$( printf "/tmp/${script_name}-%(%Y-%m-%d)T.%(%H%M%S)T.log" -1 ) exec &> >( ts "%Y-%m-%d %H:%M:%.S " | tee ${log_file} ) 

Hlavními výhodami tohoto postupu je oddělit protokolování od všeho jiného, nikoli přeplnění těla skriptu pipováním na tee nebo podobné u každého příkazu a bez nutnosti psát vlastní funkce protokolování a dodržování obyčejných starých echo.

Program ts se nachází v balíčku moreutils, který by měl být snadno dostupný za rozumných podmínek rozumný režim správy. 🙂

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *