Salva le modifiche in atto con awk


135

Sto imparando awke vorrei sapere se esiste un'opzione per scrivere le modifiche al file, in modo simile a seddove userei-i opzione per salvare le modifiche in un file.

Capisco che potrei usare il reindirizzamento per scrivere le modifiche. Tuttavia c'è un'opzione awkper farlo?


Vedere anche serverfault.com/a/547331/313521 per la risposta più generale alla "modifica di un file in atto con reindirizzamento".
Wildcard il

@Carta jolly. La soluzione è terribilmente fragile. Non esiste alcuna garanzia sull'ordinamento di eventi e l'utilizzo di tale soluzione potrebbe troncare i dati. A parte questo, non posso commentare direttamente quel sito perché ho bisogno di 50 rappresentanti su quel sito per farlo. Non capirò mai perché SO sia frammentato in Unix / Linux e server admin, et al. IMO, è stato un errore.
William Pursell,

@WilliamPursell, "nessuna garanzia sull'ordinamento degli eventi", in realtà è falso. L'unica fragilità che la soluzione ha è se la lunghezza del contenuto è maggiore della lunghezza massima per un comando. L'ordinamento degli eventi, tuttavia, è garantito.
Wildcard

@Wildcard Quale standard garantisce tale ordine?
William Pursell,

@WilliamPursell è garantito dalla documentazione di bash. Per altre conchiglie non lo so. (A proposito, se colleghi il tuo account, avrai un bonus di associazione di 100 rappresentanti e potrai commentare.)
Wildcard

Risposte:


142

Nell'ultimo GNU Awk (dalla versione 4.1.0 ), ha la possibilità di modificare i file "inplace" :

[...] L'estensione "inplace", costruita utilizzando la nuova funzione, può essere utilizzata per simulare la sed -ifunzione " " GNU . [...]

Esempio di utilizzo:

$ gawk -i inplace '{ gsub(/foo/, "bar") }; { print }' file1 file2 file3

Per mantenere il backup:

$ gawk -i inplace -v INPLACE_SUFFIX=.bak '{ gsub(/foo/, "bar") }
> { print }' file1 file2 file3

1
@sudo_O - Grazie per la dimostrazione "sul posto". Hai valutato la tua risposta!
lind

Sembra che l'opzione sia stata rimossa? Con 4.1.3, ho "-i
includefile --include

1
@Keith ho avuto la stessa domanda. L'ho appena provato e funziona sul mio 4.1.3. inplaceè in realtà una libreria inclusa con gawkbase alla risposta di iiSeymour , quindi inplaceè qualcosa che può essere incluso come includefile.
cxw,

Un avvertimento importante qui: l'array 'visto' si riempirà di righe duplicate da TUTTI i file inclusi nel comando. Quindi se ogni file ha ad esempio un'intestazione comune, questo verrà rimosso in ogni file dopo il primo. Se invece vuoi trattare ogni file in modo indipendente, dovrai fare qualcosa di simile a f in * .txt; do gawk -i inplace '! visto [$ 0] ++' "$ f"; fatto
Nick K9

136

A meno che tu non abbia GNU awk 4.1.0 o successivo ...

Non avrai un'opzione come quella di sed, -iquindi invece:

$ awk '{print $0}' file > tmp && mv tmp file

Nota: -inon è magico, sta anche creando un file temporaneo sedche lo gestisce solo per te.


A partire da GNU awk 4.1.0 ...

GNU awkaggiunta questa funzionalità nella versione 4.1.0 (rilasciata il 10/05/2013) . Non è così semplice come dare l' -iopzione come descritto nelle note rilasciate:

La nuova opzione -i (da xgawk) è usata per caricare i file della libreria awk. Ciò differisce da -f in quanto il primo argomento non opzionale è trattato come uno script.

È necessario utilizzare il inplace.awkfile di inclusione in bundle per richiamare correttamente l'estensione in questo modo:

$ cat file
123 abc
456 def
789 hij

$ gawk -i inplace '{print $1}' file

$ cat file
123
456
789

La variabile INPLACE_SUFFIXpuò essere utilizzata per specificare l'estensione per un file di backup:

$ gawk -i inplace -v INPLACE_SUFFIX=.bak '{print $1}' file

$ cat file
123
456
789

$ cat file.bak
123 abc
456 def
789 hij

Sono felice questa funzione è stata aggiunta, ma per me, l'attuazione non è molto awkish come il potere viene dalla concisione della lingua e -i inplaceè di 8 caratteri troppo lungo imo .

Ecco un link al manuale per la parola ufficiale.


Il tuo "primo" esempio non dovrebbe essere più simile a awk '{ gsub(/foo/, "bar" ) } ; { print $0 }' file > tmp.txt && mv -v tmp.txt file:?
Tony Barganski,

Con mia sorpresa, ad aprile 2019, ancora a gawk 4.0.2. Non permettere a nessuno di dirtelo e tale versione sarà disponibile.
John Lunzer,

Litte più breve awk '{print $0}' file | sponge fileusando spongeda moreutils.
brablc,

15

@sudo_O ha la risposta giusta .

Questo non può funzionare:

someprocess < file > file

La shell esegue i reindirizzamenti prima di passare il controllo a someprocess ( reindirizzamenti ). Il >reindirizzamento troncherà il file a dimensione zero ( reindirizzamento dell'output ). Pertanto, al momento dell'avvio di alcuni processi e della loro lettura dal file, non sono disponibili dati da leggere.


14

solo un piccolo trucco che funziona

echo "$(awk '{awk code}' file)" > file

Funziona come un fascino! Ma è possibile salvare il comando awk in variabile e semplicemente usarlo nel tuo trucco intelligente?
ashrasmun,

13

Un'alternativa è usare sponge:

awk '{print $0}' your_file | sponge your_file

Dove si sostituisce '{print $0}'con lo script awk e your_filecon il nome del file che si desidera modificare sul posto.

sponge assorbe completamente l'input prima di salvarlo nel file.


Quanto è standard / portatile la spugna?
Thomas,

2
spongefa parte di moreutils. Quindi non sarà presente per impostazione predefinita nella maggior parte dei sistemi. Ma sembra che almeno spongese stesso sia abbastanza portatile e possa essere eseguito quasi ovunque.
MarSoft,

1
L'aspetto negativo di questa soluzione rispetto a tee-based è che spongeleggerà tutto su RAM prima di scrivere, quindi si bloccherà su file di grandi dimensioni.
MarSoft,

5

il seguito non funzionerà

echo $(awk '{awk code}' file) > file

questo dovrebbe funzionare

echo "$(awk '{awk code}' file)" > file

3

Se desideri una soluzione solo per awk senza creare un file temporaneo e utilizzabile con la versione! = (Gawk 4.1.0):

awk '{a[b++]=$0} END {for(c=0;c<=b;c++)print a[c]>ARGV[1]}' file

4
Ma questo buffer l'intero file in memoria? Prendi in considerazione un file da 20 GB.
Amit Naidu,

0

Utilizzando tee

 awk '{awk code}' file | tee file

il teecomando ha luogo ed eseguito al termine del awkcomando a causa di |.


5
Questo non è corretto I due comandi vengono eseguiti in parallelo e i dati vengono immediatamente trasmessi in streaming attraverso la pipe. Qualsiasi file più grande del buffer (8192 byte sulla mia macchina) verrà troncato e perderai i dati.
tripflag
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.