Da das Hauptziel dieses Threads darin besteht, wie man anstelle von SAVE in NON GNU awk
vorgeht Also poste ich zuerst seine Vorlage, die jedem bei jeder Art von Anforderung helfen wird, sie müssen BEGIN
hinzufügen/anhängen und END
Abschnitt in ihrem Code, der ihren Hauptblock gemäß ihrer Anforderung beibehält, und es sollte dann die Inplace-Bearbeitung durchführen:
HINWEIS: Im Folgenden wird die gesamte Ausgabe in die Ausgabedatei geschrieben. Wenn Sie also etwas auf die Standardausgabe drucken möchten, fügen Sie bitte nur print...
hinzu Anweisung ohne > (out)
im Folgenden.
Generische Vorlage:
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
Spezielle bereitgestellte Probenlösung:
Ich habe mir in awk
folgenden Ansatz ausgedacht selbst (für hinzugefügte Beispiele folgt mein Ansatz, um dies zu lösen und die Ausgabe in Input_file selbst zu speichern)
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
HINWEIS:Dies ist nur ein Test zum Speichern der bearbeiteten Ausgabe in Input_file(s) selbst, man könnte seinen BEGIN-Abschnitt zusammen mit seinem END-Abschnitt in seinem Programm verwenden, der Hauptabschnitt sollte den spezifischen Anforderungen entsprechen selbst hinterfragen.
Faire Warnung: Da dieser Ansatz eine neue temporäre Ausgangsdatei im Pfad erstellt, stellen Sie also besser sicher, dass wir genügend Speicherplatz auf den Systemen haben, obwohl dies letztendlich nur die Haupt-Input_file(s) behält, aber während des Betriebs Platz im System/Verzeichnis benötigt
Es folgt ein Test für den obigen Code.
Programmausführung anhand eines Beispiels: Nehmen wir an, es folgen die .txt
Eingabedatei(en):
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
Wenn wir nun folgenden Code ausführen:
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
HINWEIS: Ich habe Platz ls -lhtr
in system
Abschnitt absichtlich, um zu sehen, welche Ausgabedateien erstellt werden (vorübergehend), da sie später in ihren tatsächlichen Namen umbenannt werden.
-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
Wenn wir eine ls -lhtr
machen nach awk
Skript mit dem Ausführen fertig ist, konnten wir nur noch .txt
sehen Dateien darin.
-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
Erklärung: Fügen Sie hier eine detaillierte Erklärung des obigen Befehls hinzu:
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.
Ich würde wahrscheinlich so etwas nehmen, wenn ich versuchen würde, dies zu tun:
$ 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
Ich hätte es vorgezogen, zuerst die Originaldatei in die Sicherungskopie zu kopieren und dann die Änderungen am Original zu speichern, aber dies würde den Wert der FILENAME-Variablen für jede Eingabedatei ändern, was unerwünscht ist.
Beachten Sie, dass, wenn Sie eine Originaldatei mit dem Namen whatever.bak
hatten oder whatever.new
in Ihrem Verzeichnis, dann würden Sie sie mit temporären Dateien überschreiben, also müssten Sie auch dafür einen Test hinzufügen. Ein Anruf bei mktemp
um die temporären Dateinamen zu erhalten, wäre robuster.
Das WEIT Nützlichere in dieser Situation wäre ein Tool, das jeden anderen Befehl ausführt und den "inplace" -Bearbeitungsteil ausführt, da dies verwendet werden könnte, um "inplace" -Bearbeitung für POSIX sed, awk, grep, tr, was auch immer und bereitzustellen würde es nicht erfordern, dass Sie die Syntax Ihres Skripts in print > out
ändern usw. jedes Mal, wenn Sie einen Wert drucken möchten. Ein einfaches, zerbrechliches Beispiel:
$ 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
die Sie wie folgt verwenden würden:
$ 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
Ein offensichtliches Problem mit diesem inedit
script ist die Schwierigkeit, die Eingabe-/Ausgabedateien getrennt vom Befehl zu identifizieren, wenn Sie mehrere Eingabedateien haben. Das obige Skript geht davon aus, dass alle Eingabedateien als Liste am Ende des Befehls erscheinen und der Befehl einzeln für sie ausgeführt wird, aber das bedeutet natürlich, dass Sie es nicht für Skripte verwenden können, die 2 oder mehr Dateien erfordern at eine Zeit, z. B.:
awk 'NR==FNR{a[$1];next} $1 in a' file1 file2
oder Skripte, die Variablen zwischen Dateien in der Argumentliste setzen, z. B.:
awk '{print $7}' FS=',' file1 FS=':' file2
Um es robuster zu machen, bleibt als Übung für den Leser übrig, aber schauen Sie sich den xargs
an synopsis als Ausgangspunkt dafür, wie ein robuster inedit
müsste funktionieren :-).