Dal momento che lo scopo principale di questo thread è come eseguire SAVE inplace in NON GNU awk
quindi sto pubblicando prima il suo modello che aiuterà chiunque in qualsiasi tipo di esigenza, hanno bisogno di aggiungere/aggiungere BEGIN
e END
section nel loro codice mantenendo il loro BLOCK principale secondo i loro requisiti e dovrebbe fare la modifica sul posto quindi:
NOTA: Di seguito scriverà tutto il suo output in output_file, quindi nel caso in cui desideri stampare qualcosa sullo standard output, aggiungi solo print...
istruzione senza > (out)
nel seguito.
Modello generico:
awk -v out_file="out" '
FNR==1{
close(out)
out=out_file count++
rename=(rename?rename ORS:"") "mv \047" out "\047 \047" FILENAME "\047"
}
{
.....your main block code.....
}
END{
if(rename){
system(rename)
}
}
' *.txt
Soluzione del campione specifica fornita:
Ho escogitato il seguente approccio all'interno di awk
stesso (per gli esempi aggiunti di seguito è il mio approccio per risolvere questo problema e salvare l'output in Input_file stesso)
awk -v out_file="out" '
FNR==1{
close(out)
out=out_file count++
rename=(rename?rename ORS:"") "mv \047" out "\047 \047" FILENAME "\047"
}
{
print FNR > (out)
}
END{
if(rename){
system(rename)
}
}
' *.txt
NOTA:questo è solo un test per salvare l'output modificato in Input_file(s) stesso, si potrebbe usare la sua sezione BEGIN, insieme alla sua sezione END nel loro programma, la sezione principale dovrebbe essere conforme ai requisiti di specifiche domanda stessa.
Avvertimento equo: Inoltre, poiché questo approccio crea un nuovo file temporaneo in uscita nel percorso, è meglio assicurarsi di avere spazio sufficiente sui sistemi, anche se alla fine questo manterrà solo i principali Input_file, ma durante le operazioni ha bisogno di spazio su sistema/directory
Di seguito è riportato un test per il codice precedente.
Esecuzione del programma con un esempio: Supponiamo che i seguenti siano i .txt
Input_file(s):
cat << EOF > test1.txt
onetwo three
tets testtest
EOF
cat << EOF > test2.txt
onetwo three
tets testtest
EOF
cat << EOF > test3.txt
onetwo three
tets testtest
EOF
Ora, quando eseguiamo il seguente codice:
awk -v out_file="out" '
FNR==1{
close(out)
out=out_file count++
rename=(rename?rename ORS:"") "mv \047" out "\047 \047" FILENAME "\047"
}
{
print "new_lines_here...." > (out)
}
END{
if(rename){
system("ls -lhtr;" rename)
}
}
' *.txt
NOTA: Ho posto ls -lhtr
in system
sezione intenzionalmente per vedere quali file di output sta creando (su base temporanea) perché in seguito li rinominerà con il loro nome effettivo.
-rw-r--r-- 1 runner runner 27 Dec 9 05:33 test2.txt
-rw-r--r-- 1 runner runner 27 Dec 9 05:33 test1.txt
-rw-r--r-- 1 runner runner 27 Dec 9 05:33 test3.txt
-rw-r--r-- 1 runner runner 38 Dec 9 05:33 out2
-rw-r--r-- 1 runner runner 38 Dec 9 05:33 out1
-rw-r--r-- 1 runner runner 38 Dec 9 05:33 out0
Quando eseguiamo un ls -lhtr
dopo awk
script è terminato con l'esecuzione, potremmo vedere solo .txt
file lì dentro.
-rw-r--r-- 1 runner runner 27 Dec 9 05:33 test2.txt
-rw-r--r-- 1 runner runner 27 Dec 9 05:33 test1.txt
-rw-r--r-- 1 runner runner 27 Dec 9 05:33 test3.txt
Spiegazione: Aggiunta di una spiegazione dettagliata del comando precedente qui:
awk -v out_file="out" ' ##Starting awk program from here, creating a variable named out_file whose value SHOULD BE a name of files which are NOT present in our current directory. Basically by this name temporary files will be created which will be later renamed to actual files.
FNR==1{ ##Checking condition if this is very first line of current Input_file then do following.
close(out) ##Using close function of awk here, because we are putting output to temp files and then renaming them so making sure that we shouldn't get too many files opened error by CLOSING it.
out=out_file count++ ##Creating out variable here, whose value is value of variable out_file(defined in awk -v section) then variable count whose value will be keep increment with 1 whenever cursor comes here.
rename=(rename?rename ORS:"") "mv \047" out "\047 \047" FILENAME "\047" ##Creating a variable named rename, whose work is to execute commands(rename ones) once we are done with processing all the Input_file(s), this will be executed in END section.
} ##Closing BLOCK for FNR==1 condition here.
{ ##Starting main BLOCK from here.
print "new_lines_here...." > (out) ##Doing printing in this example to out file.
} ##Closing main BLOCK here.
END{ ##Starting END block for this specific program here.
if(rename){ ##Checking condition if rename variable is NOT NULL then do following.
system(rename) ##Using system command and placing renme variable inside which will actually execute mv commands to rename files from out01 etc to Input_file etc.
}
} ##Closing END block of this program here.
' *.txt ##Mentioning Input_file(s) with their extensions here.
Probabilmente sceglierei qualcosa di simile se dovessi provare a fare questo:
$ cat ../tst.awk
FNR==1 { saveChanges() }
{ print FNR > new }
END { saveChanges() }
function saveChanges( bak, result, mkBackup, overwriteOrig, rmBackup) {
if ( new != "" ) {
bak = old ".bak"
mkBackup = "cp \047" old "\047 \047" bak "\047; echo \"$?\""
if ( (mkBackup | getline result) > 0 ) {
if (result == 0) {
overwriteOrig = "mv \047" new "\047 \047" old "\047; echo \"$?\""
if ( (overwriteOrig | getline result) > 0 ) {
if (result == 0) {
rmBackup = "rm -f \047" bak "\047"
system(rmBackup)
}
}
}
}
close(rmBackup)
close(overwriteOrig)
close(mkBackup)
}
old = FILENAME
new = FILENAME ".new"
}
$ awk -f ../tst.awk test1.txt test2.txt test3.txt
Avrei preferito copiare prima il file originale nel backup e poi operare su quel salvataggio delle modifiche all'originale, ma così facendo cambierei il valore della variabile FILENAME per ogni file di input che non è desiderabile.
Nota che se avevi un file originale chiamato whatever.bak
o whatever.new
nella tua directory, li sovrascriveresti con file temporanei, quindi dovresti aggiungere un test anche per quello. Una chiamata al mktemp
ottenere i nomi dei file temporanei sarebbe più affidabile.
La cosa MOLTO più utile da avere in questa situazione sarebbe uno strumento che esegue qualsiasi altro comando e fa la parte di modifica "sul posto" poiché potrebbe essere utilizzata per fornire modifiche "sul posto" per POSIX sed, awk, grep, tr, qualunque cosa e non ti richiederebbe di cambiare la sintassi del tuo script in print > out
ecc. ogni volta che vuoi stampare un valore. Un esempio semplice, fragile:
$ cat inedit
#!/bin/env bash
for (( pos=$#; pos>1; pos-- )); do
if [[ -f "${!pos}" ]]; then
filesStartPos="$pos"
else
break
fi
done
files=()
cmd=()
for (( pos=1; pos<=$#; pos++)); do
arg="${!pos}"
if (( pos < filesStartPos )); then
cmd+=( "$arg" )
else
files+=( "$arg" )
fi
done
tmp=$(mktemp)
trap 'rm -f "$tmp"; exit' 0
for file in "${files[@]}"; do
"${cmd[@]}" "$file" > "$tmp" && mv -- "$tmp" "$file"
done
che useresti come segue:
$ awk '{print FNR}' test1.txt test2.txt test3.txt
1
2
1
2
1
2
$ ./inedit awk '{print FNR}' test1.txt test2.txt test3.txt
$ tail test1.txt test2.txt test3.txt
==> test1.txt <==
1
2
==> test2.txt <==
1
2
==> test3.txt <==
1
2
Un ovvio problema con quel inedit
script è la difficoltà di identificare i file di input/output separatamente dal comando quando si hanno più file di input. Lo script sopra presuppone che tutti i file di input appaiano come un elenco alla fine del comando e il comando viene eseguito su di essi uno alla volta, ma ovviamente ciò significa che non puoi usarlo per script che richiedono 2 o più file a un orario, ad esempio:
awk 'NR==FNR{a[$1];next} $1 in a' file1 file2
o script che impostano variabili tra i file nell'elenco arg, ad esempio:
awk '{print $7}' FS=',' file1 FS=':' file2
Renderlo più robusto lasciato come esercizio per il lettore ma guarda al xargs
sinossi come punto di partenza per come un robusto inedit
dovrebbe funzionare :-).