Karena tujuan utama dari utas ini adalah bagaimana melakukan inplace SAVE di NON GNU awk
jadi saya memposting terlebih dahulu templatnya yang akan membantu siapa saja dalam segala jenis persyaratan, mereka perlu menambahkan/menambahkan BEGIN
dan END
bagian dalam kode mereka menjaga BLOK utama mereka sesuai kebutuhan mereka dan kemudian harus melakukan pengeditan di tempat:
CATATAN: Mengikuti akan menulis semua outputnya ke output_file, jadi jika Anda ingin mencetak apa pun ke output standar, harap tambahkan saja print...
pernyataan tanpa > (out)
dalam mengikuti.
Templat Umum:
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
Solusi sampel khusus yang diberikan:
Saya telah menemukan pendekatan berikut dalam awk
itu sendiri (untuk sampel tambahan berikut ini adalah pendekatan saya untuk menyelesaikan ini dan menyimpan output ke dalam Input_file itu sendiri)
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
CATATAN:ini hanya tes untuk menyimpan keluaran yang telah diedit ke dalam Input_file(s) itu sendiri, seseorang dapat menggunakan bagian BEGIN-nya, bersama dengan bagian END-nya dalam program mereka, bagian utama harus sesuai dengan persyaratan khusus pertanyaan itu sendiri.
Peringatan wajar: Juga karena pendekatan ini membuat file keluar sementara baru di jalur jadi lebih baik pastikan kita memiliki cukup ruang pada sistem, meskipun pada hasil akhir ini hanya akan menyimpan Input_file utama tetapi selama operasi dibutuhkan ruang pada sistem/direktori
Berikut adalah tes untuk kode di atas.
Eksekusi program dengan contoh: Mari kita asumsikan berikut ini adalah .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
Sekarang ketika kita menjalankan kode berikut:
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
CATATAN: Saya punya tempat ls -lhtr
di system
bagian sengaja untuk melihat file keluaran mana yang dibuatnya (berbasis sementara) karena nanti akan mengubah namanya menjadi nama aslinya.
-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
Saat kita melakukan ls -lhtr
setelah awk
skrip selesai dijalankan, kita hanya bisa melihat .txt
file di sana.
-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
Penjelasan: Menambahkan penjelasan mendetail tentang perintah di atas di sini:
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.
Saya mungkin akan menggunakan sesuatu seperti ini jika saya mencoba melakukan ini:
$ 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
Saya lebih suka menyalin file asli ke cadangan terlebih dahulu dan kemudian mengoperasikan penyimpanan perubahan ke aslinya, tetapi hal itu akan mengubah nilai variabel FILENAME untuk setiap file masukan yang tidak diinginkan.
Perhatikan bahwa jika Anda memiliki file asli bernama whatever.bak
atau whatever.new
di direktori Anda maka Anda akan menimpanya dengan file temp sehingga Anda perlu menambahkan tes untuk itu juga. Panggilan ke mktemp
untuk mendapatkan nama file temp akan lebih kuat.
Hal yang JAUH lebih berguna untuk dimiliki dalam situasi ini adalah alat yang mengeksekusi perintah lain dan melakukan bagian pengeditan "inplace" karena itu dapat digunakan untuk menyediakan pengeditan "inplace" untuk POSIX sed, awk, grep, tr, apapun dan tidak mengharuskan Anda mengubah sintaks skrip Anda menjadi print > out
dll. setiap kali Anda ingin mencetak nilai. Contoh sederhana, rapuh,:
$ 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
yang akan Anda gunakan sebagai berikut:
$ 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
Satu masalah yang jelas dengan inedit
itu skrip adalah kesulitan mengidentifikasi file input/output secara terpisah dari perintah ketika Anda memiliki banyak file input. Skrip di atas mengasumsikan semua file input muncul sebagai daftar di akhir perintah dan perintah dijalankan terhadapnya satu per satu, tetapi tentu saja itu berarti Anda tidak dapat menggunakannya untuk skrip yang memerlukan 2 file atau lebih di suatu waktu, mis.:
awk 'NR==FNR{a[$1];next} $1 in a' file1 file2
atau skrip yang menetapkan variabel di antara file dalam daftar arg, mis.:
awk '{print $7}' FS=',' file1 FS=':' file2
Membuatnya lebih kuat kiri sebagai latihan untuk pembaca tetapi lihat xargs
sinopsis sebagai titik awal untuk bagaimana inedit
yang tangguh perlu bekerja :-).