简体   繁体   中英

sed/awk/perl formatting multiple paragraphs

dzinfo gives me output formatted like this;

User: x0000001
Forced into restricted environment: No

  Role Name        Avail Restricted Env 
  ---------------  ----- -------------- 
  login/Corp_All   Yes   None           
  _Example-Role--  Yes   _Example-Role-- 
  ALL_Servers-Win        ALL_Servers-Win 
  /US_All                /US_All  
  Domain_GLOBAL-Ro Yes   Domain_GLOBAL-Ro 
  le-CORE_Group-AL       le-CORE_Group-AL 
  L-MacOS/Domain_        L-MacOS/Domain_
  GLOBAL                 GLOBAL      

    Effective rights:
    Password login
    Non password login
    Allow normal shell


PAM Application  Avail Source Roles         
---------------  ----- -------------------- 
*                Yes   login/US_All         
Privileged commands:
  Name             Avail Command               Source Roles         
  ---------------  ----- --------------------  -------------------- 
  CORP_GLOBAL-Com  Yes   /usr/bin/getfacl      CORP_GLOBAL-Role-COR 
  mand-CORE_SVR_I                              E_SVR_INFRA_ALL-LNX/ 
  NFRA_ALL-V042-S                              CORP_GLOBAL          
  00042/CORP_GLOB                                                   
  AL                                                                
  CORP_GLOBAL-Com  Yes   /usr/bin/dzdo -l      CORP_GLOBAL-Role-COR 
  mand-CORE_SVR_I                              E_SVR_INFRA_ALL-LNX/ 
  NFRA_ALL-V042-S                              CORP_GLOBAL          
  00048/CORP_GLOB                                                   
  AL                                                                
  CORP_GLOBAL-Com  Yes   /bin/cp temp_auth     CORP_GLOBAL-Role-COR 
  mand-CORE_SVR_I        /home/sudocfg/author  E_SVR_INFRA_ALL-LNX/ 
  NFRA_ALL-V042-S        ized_keys             CORP_GLOBAL          
  00085/CORP_GLOB                                                   
  AL                                                                

What tool would be the best choice to format a report like this? And how could I match/combine and format the columns/lines to something like the following?

User: x0000001
Forced into restricted environment: No

  Role Name                                             Avail   Restricted Env 
  ---------------                                       -----   -------------- 
  login/Corp_All                                        Yes     None           
  _Example-Role--ALL_Servers-Win/US_All                 Yes     _Example-Role--ALL_Servers-Win/US_All 
  Domain_GLOBAL-Role-CORE_Group-ALL-MacOS/Domain_GLOBAL Yes     Domain_GLOBAL-Role-CORE_Group-ALL-MacOS/Domain_GLOBAL 

    Effective rights:
    Password login
    Non password login
    Allow normal shell

  PAM Application  Avail Source Roles         
  ---------------  ----- -------------------- 
  *                Yes   login/US_All         

Privileged commands:
  Name                                                              Avail   Command                                             Source Roles        
  ---------------                                                   -----   --------------------                                -------------------- 
  CORP_GLOBAL-Command-CORE_SVR_INFRA_ALL-V042-S00042/CORP_GLOBAL    Yes     /usr/bin/getfacl                                    CORP_GLOBAL-Role-CORE_SVR_INFRA_ALL-LNX/CORP_GLOBAL
  CORP_GLOBAL-Command-CORE_SVR_INFRA_ALL-V042-S00048/CORP_GLOBAL    Yes     /usr/bin/dzdo -l                                    CORP_GLOBAL-Role-CORE_SVR_INFRA_ALL-LNX/CORP_GLOBAL 
  CORP_GLOBAL-Command-CORE_SVR_INFRA_ALL-V042-S00085/CORP_GLOBAL    Yes     /bin/cp temp_auth /home/sudocfg/authorized_keys     CORP_GLOBAL-Role-CORE_SVR_INFRA_ALL-LNX/CORP_GLOBAL

The text in each column can vary greatly, so I'd like to have the width automatically adjust.

I can handle one-liners, but for a report like this? I wouldn't know where to even begin.

To get you started, here's basically how you'd start to isolate the individual fields on each line using GNU awk for FIELDWIDTHS:

$ cat tst.awk
BEGIN { origFS=FS }
/---/ {
    origFS=FS
    split($0,f,/\s+|-+/,s)
    FIELDWIDTHS=""
    for (i=1; i in s; i++) {
        FIELDWIDTHS = (i>1 ? FIELDWIDTHS " " : "") length(s[i])
    }
}
/^\s*$/ {
    FIELDWIDTHS=""
    FS=origFS
}
{
    for (i=1; i<=NF; i++) {
        printf "<%s>", $i, (i<NF?OFS:ORS)
    }
    print ""
}

.

$ awk -f tst.awk file
<User:><x0000001>
<Forced><into><restricted><environment:><No>

<Role><Name><Avail><Restricted><Env>
<---------------><-----><-------------->
<  ><login/Corp_All ><  ><Yes  >< ><None          >< >
<  ><_Example-Role--><  ><Yes  >< ><_Example-Role-><->
<  ><ALL_Servers-Win><  ><     >< ><ALL_Servers-Wi><n>
<  ></US_All        ><  ><     >< ></US_All  ><>
<  ><Domain_GLOBAL-R><o ><Yes  >< ><Domain_GLOBAL-><R>
<  ><le-CORE_Group-A><L ><     >< ><le-CORE_Group-><A>
<  ><L-MacOS/Domain_><  ><     >< ><L-MacOS/Domain><_>
<  ><GLOBAL         ><  ><     >< ><GLOBAL      ><>

<Effective><rights:>
<Password><login>
<Non><password><login>
<Allow><normal><shell>


<PAM><Application><Avail><Source><Roles>
<---------------><-----><-------------------->
<*              ><  ><Yes  >< ><login/US_All        >< >
<Privileged comm><an><ds:><><><>
<  Name         ><  ><  Ava><i><l Command           >< >
<  -------------><--><  ---><-><- ------------------><->
<  ><CORP_GLOBAL-Com><  ><Yes  >< ></usr/bin/getfacl    ><  ><CORP_GLOBAL-Role-COR>< >
<  ><mand-CORE_SVR_I><  ><     >< ><                    ><  ><E_SVR_INFRA_ALL-LNX/>< >
<  ><NFRA_ALL-V042-S><  ><     >< ><                    ><  ><CORP_GLOBAL         >< >
<  ><00042/CORP_GLOB><  ><     >< ><                    ><  ><                    >< >
<  ><AL             ><  ><     >< ><                    ><  ><                    >< >
<  ><CORP_GLOBAL-Com><  ><Yes  >< ></usr/bin/dzdo -l    ><  ><CORP_GLOBAL-Role-COR>< >
<  ><mand-CORE_SVR_I><  ><     >< ><                    ><  ><E_SVR_INFRA_ALL-LNX/>< >
<  ><NFRA_ALL-V042-S><  ><     >< ><                    ><  ><CORP_GLOBAL         >< >
<  ><00048/CORP_GLOB><  ><     >< ><                    ><  ><                    >< >
<  ><AL             ><  ><     >< ><                    ><  ><                    >< >
<  ><CORP_GLOBAL-Com><  ><Yes  >< ></bin/cp temp_auth   ><  ><CORP_GLOBAL-Role-COR>< >
<  ><mand-CORE_SVR_I><  ><     >< ></home/sudocfg/author><  ><E_SVR_INFRA_ALL-LNX/>< >
<  ><NFRA_ALL-V042-S><  ><     >< ><ized_keys           ><  ><CORP_GLOBAL         >< >
<  ><00085/CORP_GLOB><  ><     >< ><                    ><  ><                    >< >
<  ><AL             ><  ><     >< ><               ><><><>

You're going to want to buy the book Effective Awk Programming, 4th Edition, by Arnold Robbins and have it handy for reference as you start tinkering with that to see how it works and then building on it.

I can handle one-liners, but for a report like this? I wouldn't know where to even begin.

Indeed this seems hardly possible with a one-liner (unless it's one very long line). But to begin with, the problem can be analyzed and a possible solution outlined.

  • Identify what parts of the input constitute the tables (where column lines are to be combined): We could use the lines of hyphens between column headers and text to identify the start of a table, and a short line or an unfitting line, where the gaps between the columns are not blank, to identify the end.
  • Read a table in line by line, split each line into the columns (whose margins can be derived from the hyphen lines), and, if the current line is a wrapped continuation line of the preceding line (ie if it has empty columns), concatenate each column's texts. In the course of this, the required column widths can be determined from the maximum combined text lengths.
  • Write the table out, using the before determined column widths.

The following Perl program implements a solution. Note however that, since it uses seek , it cannot work on piped input, but only on a file.

#!/usr/bin/perl -wn
if (/---/)  # here comes a table
{
    seek ARGV, $priprior, 0;    # set position back to header line
    push @gap, $-[1] while /(?<=-) *( )-/g; # find the column gaps
    $tabrow = -1;               # index of current table row: no rows yet
    @maxlng = (0)x($#gap+2);    # maximum length of text in each column
    while (<>)                  # read the table, starting with headers
    {   # check for end of table:
        last if length() <= $gap[-1] or substr($_, $gap[-1], 1) ne ' ';
        $offset = 0;    # first column start
        $contin = 0;    # is it a continuation line?
        foreach $i (0..$#gap+1)
        {   # extract column:
            if ($i <= $#gap)    # not last column?
            {   $column[$i] = substr $_, $offset, $gap[$i]-$offset;
                $offset = $gap[$i];
            }
            else                # last column
            {   $column[$i] = substr $_, $offset;
            }
            $column[$i] =~ s/\s+$//;        # remove trailing whitespace
            $contin += $column[$i] eq ''    # column empty?
        }
        ++$tabrow unless $contin;
        foreach $i (0..$#gap+1)
        {   # ugly fix to restore the space in "temp_auth /home/...", where
            # the column is wrapped early: --------------------
                                         # ...
                                         # /bin/cp temp_auth   
                                         # /home/sudocfg/author
                                         # ized_keys           
            $table[$tabrow][$i] .= ' ' if $contin and length($table[1][$i])>
                                                length($table[$tabrow][$i]);
            # now that's fixed, proceed with normal unwrapping of the column
            $column[$i] =~ s/^ +// if $contin or $i;    # remove leading spaces
            $table[$tabrow][$i] .= $column[$i];
               $maxlng[$i] = length $table[$tabrow][$i] # update maximum length
            if $maxlng[$i] < length $table[$tabrow][$i];
        }
        undef $_; last if eof
    }
    foreach $e (@table)
    {
        foreach $i (0..$#gap+1) { printf "%-*s", $maxlng[$i]+1, $$e[$i]; }
        print "\n";
    }
    undef @gap; undef @table;
}
else { print $previous_line if $previous_line }
$previous_line = $_;
$priprior = $prior;
$prior = tell ARGV;

END { print $previous_line if $previous_line }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM