GNU/Linux >> Belajar Linux >  >> Linux

Simpan modifikasi di tempat dengan NON GNU awk

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 :-).


Linux
  1. Partisi drive di Linux dengan GNU Parted

  2. Memulai dengan awk, alat pengurai teks yang andal

  3. Mengekstrak dan menampilkan data dengan awk

  1. perintah whoami di Linux dijelaskan dengan contoh

  2. Hitung Catatan Pencocokan Pola Dengan Awk?

  3. Ganti Nama Mass File Bash Dengan Penghitung?

  1. Tangkap Grup Dengan Awk Atau Grep?

  2. Nilai persentase dengan GNU Diff

  3. menggunakan awk dengan kondisi nilai kolom