Использование AWK для слияния двух файлов по почте с образованием отформатированного третьего файла - PullRequest
0 голосов
/ 02 августа 2020

Это не такой уж большой вопрос. Это и мой первый пост. Я не новичок ie, но я только начинаю изучать awk.

Недавно мне пришлось сгенерировать некоторые. xml файлы конфигурации из двух наборов данных, которые изначально не сохраняются as xml.

Я много искал помощь AWK, но понимаю, что 99% предоставленных скриптов используют продвинутые методы AWK, что затрудняет понимание новичками. Я верю, что это и отвлечет интерес, и повысит кривую обучения.

EG. awk '{/ ERROR /}' </ var / log / messages </p>

Человеку, который не выполняет много сценариев awk, не очень легко узнать, что там происходит, но он делает множество.

Итак, здесь я собираюсь предоставить новый ie подход для выполнения такой задачи. Взамен

Я хотел бы получить предложения по

  1. более оптимизированной версии newb ie.
  2. оптимизированной расширенной версии с надлежащим объяснением, которое поможет при переходе .

$. / Test1.awk Samfig2.cfg user1.tsv $ ls cfg * cfg2ZR6ZS29XXOF. xml cfg42IXEIGOQ0FG. xml cfg759YUZKTS368CP. xml cfgNTQUE * 1057AL * cfgNTX *

test1.awk

#!/usr/bin/awk -f
BEGIN { 
        configfile=ARGV[1]
        Userfile=ARGV[2]
      
        if (ARGV[2] == "") {
                        print "ERROR: Need two files Usage "ENVIRON["_"]" Config.cfg Users.tsv" >"/dev/stderr"
                        exit }
       ARGV[1] = ""   # We want to control the manipulation of files
       ARGV[2] = ""
        FS = "=" ;  # this is being done dynamically, no need here (oh yes setting here cause almost 90% execution reduction)
        getline Header < Userfile;  # advance the Header line and get the headers
         gsub("\r","",Header);  # My production version doesnt need this but the sample data seem to include \r on the end field
        HeaderN=split(Header,Headarray,"\t");

# Expand begin block to include {} below to prevent pause for input       }
#{   

while ((getline User < Userfile) >0 )  # Read row from field into variable User do all the blocks below based on the number of records in Userfile.
   { 
    gsub("\r","",User); # My production version doesnt need this but the sample data seem to include \r on the end field
    n=split(User,Detailsarray,"\t");           # split row stored in User into array called Detailsarray n stores the total number of elements with FS =\t
    filetostore=("cfg" Detailsarray[HeaderN] ".xml"); # Were are storing each file based on Last Header value in the user file
    Recordtmp=""                               #To reduce file IO will append to string then output later. 
    Recordtmp ="<?xml version=\42""1.0\42 encoding=\42utf-8\42?>";   #\42 is the double quote ". Result is  <?xml version="1.0" encoding="utf-8"?>  
                                                                #without the "" set you would get <?xml version=.0" encoding="utf-8"?> as it would interpret as \421 
    Recordtmp = Recordtmp "\n<users_provision version=\42""1\42>";
    Recordtmp = Recordtmp "\n<config version=\42""1\42>"; 
    
    for(i=1; i<=HeaderN; i++)  # We could also use n instead of HeaderN but just incase I'm maintaining base on the initial header
        Recordtmp = Recordtmp  "\n    <" Headarray[i] ">" Detailsarray[i] "</" Headarray[i] ">"; 
                      
    while ((getline < configfile) >0 )
        {    
            Recordtmp = Recordtmp  "\n    <" $1 ">" $2 "</" $1 ">";
         }
    Recordtmp = Recordtmp  "\n</config>"; 
    Recordtmp = Recordtmp  "\n</users_provision>\n";
    
    close(configfile);
   
    print (Recordtmp)> filetostore;
    close(filetostore);     

   }
#}

# END {  # Had to expand begin block to avoid pause issue
      close(Userfile);
     }

Samfig.cfg

URL=msn.com
Dealer=RealtorSales
SQRFT=3600
Taxes=6000
Asking=1,800,000
Built=July/2019
Listed=07/12/2109
MSRP=2,000,000
Kitchen=5
Baths=2.5
floors=3
Rooms=5

user1.tsv

Name    StreeNum    StreetName  City    State   ZIP IDcard
Ashanti Simmons 138 Jockey Hollow Avenue    Phillipsburg    NJ  08865   2ZR6ZS29XXOF
Bobby Marshall  7985 E.     Beech Road  Flemington  NJ  08822   YDMWJVLO6YWS
Marianna Quinn  8950    Main St.    Moses Lake  WA  98837   42IXEIGOQ0FG
Jaslyn Fuentes  9581    Lafayette Dr.   Hummelstown PA  17036   NTQALYCPLE06
Cory Jordan 26  Randall Mill Street Bay City    MI  48706   759YUZKTS368

Содержимое cfg2ZR6ZS29XXOF. xml

<?xml version="1.0" encoding="utf-8"?>
<users_provision version="1">
<config version="1">
    <Name>Ashanti Simmons</Name>
    <StreeNum>138</StreeNum>
    <StreetName>Jockey Hollow Avenue</StreetName>
    <City>Phillipsburg</City>
    <State>NJ</State>
    <ZIP>08865</ZIP>
    <IDcard>2ZR6ZS29XXOF</IDcard>
    <URL>msn.com</URL>
    <Dealer>RealtorSales</Dealer>
    <SQRFT>3600</SQRFT>
    <Taxes>6000</Taxes>
    <Asking>1,800,000</Asking>
    <Built>July/2019</Built>
    <Listed>07/12/2109</Listed>
    <MSRP>2,000,000</MSRP>
    <Kitchen>5</Kitchen>
    <Baths>2.5</Baths>
    <floors>3</floors>
    <Rooms>5</Rooms>
</config>
</users_provision>

Эти улучшения можно было бы сделать.

  1. Читать значения FS / разделить на переменные из командной строки.
  2. Заменить значение по умолчанию, которое существует в файле конфигурации, только если оно не равно нулю в файле данных.

Ответы [ 2 ]

2 голосов
/ 02 августа 2020

что-то вроде этого?

$ awk 'function bt(t)    {return "<"t">"}
       function et(t)    {return bt("/"t)}
       function tag(t,v) {return bt(t) v et(t)}
       function prolog() {return bt("?xml version=\"1.0\" encoding=\"utf-8\"?")}
       function start(t) {return bt(t " version=\"1\"")}

       NR==FNR {split($0,a,"="); ks[NR]=a[1]; vs[NR]=a[2]; nk=NR; next}
       FNR==1  {n=split($0,header); next}
               {file="cfg" $NF ".xml"
                print prolog() > file
                print start("users_provision") > file
                print start("config") > file
                for(i=1;i<=NF;i++) print "\t" tag(header[i],$i) > file
                for(i=1;i<=nk;i++) print "\t" tag(ks[i], vs[i]) > file
                print et("config") > file
                print et("user_provision") > file
                close(file)}' config FS='\t' user

с некоторой вспомогательной функцией для простой основной части кода. Однако никаких проверок ошибок или проверок.

производит

$ cat cfg2ZR6ZS29XXOF.xml

<?xml version="1.0" encoding="utf-8"?>
<users_provision version="1">
<config version="1">
        <Name>Ashanti Simmons</Name>
        <StreeNum>138</StreeNum>
        <StreetName>Jockey Hollow Avenue</StreetName>
        <City>Phillipsburg</City>
        <State>NJ</State>
        <ZIP>08865</ZIP>
        <IDcard>2ZR6ZS29XXOF</IDcard>
        <URL>msn.com</URL>
        <Dealer>RealtorSales</Dealer>
        <SQRFT>3600</SQRFT>
        <Taxes>6000</Taxes>
        <Asking>1,800,000</Asking>
        <Built>July/2019</Built>
        <Listed>07/12/2109</Listed>
        <MSRP>2,000,000</MSRP>
        <Kitchen>5</Kitchen>
        <Baths>2.5</Baths>
        <floors>3</floors>
        <Rooms>5</Rooms>
</config>
</user_provision>

Если вы не работаете с файлами большого размера, я думаю, что оптимизация должна быть связана с простотой обслуживания. Хотя большинство сценариев awk эфемерны, при правильной структуре и комментариях они могут быть полезны в течение длительного времени.

1 голос
/ 02 августа 2020

Похоже, вы упустили главный смысл awk, который заключается в том, что он читает входной файл (ы) за вас, и поэтому вы написали сценарий awk так, как если бы вы написали программу C с кучей while-read циклы в разделе BEGIN, чтобы вручную делать то, что awk делает автоматически. Я думаю, что это то, что вы пытаетесь сделать:

$ cat tst.awk
BEGIN {
    FS = "\t"
    fmt = "   <%s>%s</%s>\n"
}
{ sub(/\r$/,"") }
NR == FNR {
    tag = val = $0
    sub(/=.*/,"",tag)
    sub(/[^=]+=/,"",val)
    comm = comm sprintf(fmt, tag, val, tag)
    next
}
FNR == 1 {
    for (i=1; i<=NF; i++) {
        tags[i] = $i
    }
    next
}
{
    out = "cfg" $NF ".xml"

    print "<?xml version=\"1.0\" encoding=\"utf-8\"?>"  > out
    print "<users_provision version=\"1\">"             > out
    print "<config version=\"1\">"                      > out

    for (i=1; i<=NF; i++) {
        printf fmt, tags[i], $i, tags[i]                > out
    }

    printf "%s", comm                                   > out

    print "</config>"                                   > out
    print "</users_provision>"                          > out

    close(out)
}

.

$ awk -f tst.awk Samfig.cfg user1.tsv

.

$ head -50 cfg*.xml
==> cfg2ZR6ZS29XXOF.xml <==
<?xml version="1.0" encoding="utf-8"?>
<users_provision version="1">
<config version="1">
   <Name>Ashanti Simmons</Name>
   <StreeNum>138</StreeNum>
   <StreetName>Jockey Hollow Avenue</StreetName>
   <City>Phillipsburg</City>
   <State>NJ</State>
   <ZIP>08865</ZIP>
   <IDcard>2ZR6ZS29XXOF</IDcard>
   <URL>msn.com</URL>
   <Dealer>RealtorSales</Dealer>
   <SQRFT>3600</SQRFT>
   <Taxes>6000</Taxes>
   <Asking>1,800,000</Asking>
   <Built>July/2019</Built>
   <Listed>07/12/2109</Listed>
   <MSRP>2,000,000</MSRP>
   <Kitchen>5</Kitchen>
   <Baths>2.5</Baths>
   <floors>3</floors>
   <Rooms>5</Rooms>
</config>
</users_provision>

==> cfg42IXEIGOQ0FG.xml <==
<?xml version="1.0" encoding="utf-8"?>
<users_provision version="1">
<config version="1">
   <Name>Marianna Quinn</Name>
   <StreeNum>8950</StreeNum>
   <StreetName>Main St.</StreetName>
   <City>Moses Lake</City>
   <State>WA</State>
   <ZIP>98837</ZIP>
   <IDcard>42IXEIGOQ0FG</IDcard>
   <URL>msn.com</URL>
   <Dealer>RealtorSales</Dealer>
   <SQRFT>3600</SQRFT>
   <Taxes>6000</Taxes>
   <Asking>1,800,000</Asking>
   <Built>July/2019</Built>
   <Listed>07/12/2109</Listed>
   <MSRP>2,000,000</MSRP>
   <Kitchen>5</Kitchen>
   <Baths>2.5</Baths>
   <floors>3</floors>
   <Rooms>5</Rooms>
</config>
</users_provision>

==> cfg759YUZKTS368.xml <==
<?xml version="1.0" encoding="utf-8"?>
<users_provision version="1">
<config version="1">
   <Name>Cory Jordan</Name>
   <StreeNum>26</StreeNum>
   <StreetName>Randall Mill Street</StreetName>
   <City>Bay City</City>
   <State>MI</State>
   <ZIP>48706</ZIP>
   <IDcard>759YUZKTS368</IDcard>
   <URL>msn.com</URL>
   <Dealer>RealtorSales</Dealer>
   <SQRFT>3600</SQRFT>
   <Taxes>6000</Taxes>
   <Asking>1,800,000</Asking>
   <Built>July/2019</Built>
   <Listed>07/12/2109</Listed>
   <MSRP>2,000,000</MSRP>
   <Kitchen>5</Kitchen>
   <Baths>2.5</Baths>
   <floors>3</floors>
   <Rooms>5</Rooms>
</config>
</users_provision>

==> cfgNTQALYCPLE06.xml <==
<?xml version="1.0" encoding="utf-8"?>
<users_provision version="1">
<config version="1">
   <Name>Jaslyn Fuentes</Name>
   <StreeNum>9581</StreeNum>
   <StreetName>Lafayette Dr.</StreetName>
   <City>Hummelstown</City>
   <State>PA</State>
   <ZIP>17036</ZIP>
   <IDcard>NTQALYCPLE06</IDcard>
   <URL>msn.com</URL>
   <Dealer>RealtorSales</Dealer>
   <SQRFT>3600</SQRFT>
   <Taxes>6000</Taxes>
   <Asking>1,800,000</Asking>
   <Built>July/2019</Built>
   <Listed>07/12/2109</Listed>
   <MSRP>2,000,000</MSRP>
   <Kitchen>5</Kitchen>
   <Baths>2.5</Baths>
   <floors>3</floors>
   <Rooms>5</Rooms>
</config>
</users_provision>

==> cfgYDMWJVLO6YWS.xml <==
<?xml version="1.0" encoding="utf-8"?>
<users_provision version="1">
<config version="1">
   <Name>Bobby Marshall</Name>
   <StreeNum>7985</StreeNum>
   <StreetName>E. Beech Road</StreetName>
   <City>Flemington</City>
   <State>NJ</State>
   <ZIP>08822</ZIP>
   <IDcard>YDMWJVLO6YWS</IDcard>
   <URL>msn.com</URL>
   <Dealer>RealtorSales</Dealer>
   <SQRFT>3600</SQRFT>
   <Taxes>6000</Taxes>
   <Asking>1,800,000</Asking>
   <Built>July/2019</Built>
   <Listed>07/12/2109</Listed>
   <MSRP>2,000,000</MSRP>
   <Kitchen>5</Kitchen>
   <Baths>2.5</Baths>
   <floors>3</floors>
   <Rooms>5</Rooms>
</config>
</users_provision>

При чтении файла .cfg я заполняю / используйте переменные tag и val, как это делаю я, вместо того, чтобы устанавливать для FS значение =, а затем использовать $1 и $2 или аналогичные, так что сценарий будет успешным, даже если любое значение содержит =, например, Dealer=List=>Sold.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...