GNU/Linux >> Znalost Linux >  >> Linux

Uložte úpravy na místě pomocí NON GNU awk

Protože hlavním cílem tohoto vlákna je, jak provést SAVE v NON GNU awk takže nejprve zveřejňuji jeho šablonu, která pomůže komukoli v jakémkoli požadavku, musí přidat/přidat BEGIN a END sekce v jejich kódu, přičemž si zachová svůj hlavní BLOK podle svých požadavků a poté by měla provést místní úpravu:

POZNÁMKA: Následující zapíše celý výstup do výstupního_souboru, takže v případě, že chcete cokoliv vytisknout na standardní výstup, přidejte pouze print... příkaz bez > (out) v následujícím.

Obecná šablona:

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

Konkrétní poskytnuté ukázkové řešení:

V rámci awk jsem přišel s následujícím přístupem sám (pro přidané vzorky následuje můj přístup, jak to vyřešit a uložit výstup do samotného Input_file)

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

POZNÁMKA:toto je pouze test pro uložení upraveného výstupu do samotného souboru Input_file(s), lze použít jeho sekci BEGIN spolu s sekcí END v jejich programu, hlavní sekce by měla odpovídat požadavku konkrétního samotná otázka.

Spravedlivé varování: Vzhledem k tomu, že tento přístup vytváří nový dočasný out soubor v cestě, je lepší se ujistit, že máme v systémech dostatek místa, i když v konečném důsledku to ponechá pouze hlavní vstupní_soubory, ale během operací potřebuje místo v systému/adresáři

Následuje test výše uvedeného kódu.

Spuštění programu s příkladem: Předpokládejme, že následující jsou .txt Vstupní_soubor(y):

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

Nyní, když spustíme následující kód:

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

POZNÁMKA: Mám místo ls -lhtr v system sekce záměrně vidět, které výstupní soubory vytváří (dočasný základ), protože je později přejmenuje na jejich skutečný název.

-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

Když uděláme ls -lhtr po awk skript je dokončen a běží, viděli jsme pouze .txt soubory tam.

-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

Vysvětlení: Zde přidáváme podrobné vysvětlení výše uvedeného příkazu:

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.

Pravděpodobně bych šel s něčím takovým, kdybych se pokusil udělat toto:

$ 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

Upřednostnil bych nejprve zkopírovat původní soubor do zálohy a poté provést uložení změn do originálu, ale tím by se změnila hodnota proměnné FILENAME pro každý vstupní soubor, což je nežádoucí.

Všimněte si, že pokud jste měli původní soubory s názvem whatever.bak nebo whatever.new ve vašem adresáři byste je pak přepsali dočasnými soubory, takže byste pro to museli přidat test. Volání na číslo mktemp získat názvy dočasných souborů by bylo robustnější.

MNOHEM užitečnější věcí, kterou byste v této situaci měli, by byl nástroj, který provede jakýkoli jiný příkaz a provede „inplace“ editační část, protože to by mohlo být použito k poskytnutí „inplace“ editace pro POSIX sed, awk, grep, tr, cokoliv a nebude vyžadovat, abyste změnili syntaxi skriptu na print > out atd. pokaždé, když chcete vytisknout hodnotu. Jednoduchý, křehký příklad:

$ 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

který byste použili následovně:

$ 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

Jeden zřejmý problém s tím inedit script je obtížnost identifikace vstupních/výstupních souborů odděleně od příkazu, když máte více vstupních souborů. Výše uvedený skript předpokládá, že všechny vstupní soubory se objeví jako seznam na konci příkazu a příkaz je spouštěn proti nim jeden po druhém, ale to samozřejmě znamená, že jej nemůžete použít pro skripty, které vyžadují 2 nebo více souborů na čas, např.:

awk 'NR==FNR{a[$1];next} $1 in a' file1 file2

nebo skripty, které nastavují proměnné mezi soubory v seznamu argumentů, např.:

awk '{print $7}' FS=',' file1 FS=':' file2

Zvětšení robustnosti ponecháno jako cvičení pro čtenáře, ale podívejte se na xargs synopse jako výchozí bod pro to, jak robustní inedit potřebovalo by pracovat :-).


Linux
  1. Počítejte v linuxovém shellu s GNU bc

  2. Počítat záznamy odpovídající vzoru s Awk?

  3. Hromadné přejmenování souboru Bash pomocí čítače?

  1. pomocí awk s podmínkami hodnoty sloupce

  2. Vykreslete soubor .gnu pomocí gnuplot

  3. Jak odečíst řádky (řádky) pomocí AWK

  1. Rozdělte disk na Linuxu pomocí GNU Parted

  2. Začínáme s awk, výkonným nástrojem pro analýzu textu

  3. Extrahování a zobrazení dat pomocí awk