0

I can use stat to create an ls output that shows both formats of permission information which can be handy:

stat --printf="%A\t%a\t%h\t%U\t%G\t%s\t%.19y\t%n\n" . .*

drwxr-xr-x      755     4       boss    boss    4096    2021-10-29 22:49:12     .
drwxr-xr-x      755     4       boss    boss    4096    2021-10-29 22:49:12     .
drwxr-xr-x      755     36      boss    boss    4096    2021-11-01 11:30:24     ..
-rw-r--r--      644     1       boss    boss    97708   2021-11-01 11:30:16     .custom
-rw-r--r--      644     1       boss    boss    4013    2021-10-11 22:04:04     .custom-dk

However, the spacing between columns uses \t which is fine, but quite 'gappy'. This made me curious, is there a generic way to post-process any outputs like this such that the columns will be at the lowest common denominator of one-space gaps, i.e. is there a generic way to adjust the above to something like the below using awk or sed or similar (I'm also right-justifying just the number columns as an 'ideal' output, if that's possible)?

drwxr-xr-x 755  4 boss boss  4096 2021-10-29 22:49:12 .
drwxr-xr-x 755  4 boss boss  4096 2021-10-29 22:49:12 .
drwxr-xr-x 755 36 boss boss  4096 2021-11-01 11:30:24 ..
-rw-r--r-- 644  1 boss boss 97708 2021-11-01 11:30:16 .custom
-rw-r--r-- 644  1 boss boss  4013 2021-10-11 22:04:04 .custom-dk
1
  • 2
    Piping that to column -t reduces the space a bit, but not as much as your desired output
    – muru
    Nov 1, 2021 at 12:12

2 Answers 2

1

Regarding "or sed" in your question - sed is the right tool to use for simple s/old/new/ transformations on individual strings, what you're doing is not anything like that and so sed shouldn't even be an option to consider.

Using a 2-pass approach, first to determine the max width and alignment for every column and then to use them when printing, using any awk in any shell on every Unix box:

$ cat tst.awk
BEGIN { FS="\t" }
NR==FNR {
    for (i=1; i<=NF; i++) {
        align[i] = ( $i ~ /^[0-9]+$/ ? "" : "-" )
        width[i] = ( length($i) > width[i] ? length($i) : width[i] )
    }
    next
}
{
    for (i=1; i<NF; i++) {
        printf "%" align[i] width[i] "s ", $i
    }
    print $NF
}

$ awk -f tst.awk file file
drwxr-xr-x 755  4 boss boss  4096 2021-10-29 22:49:12 .
drwxr-xr-x 755  4 boss boss  4096 2021-10-29 22:49:12 .
drwxr-xr-x 755 36 boss boss  4096 2021-11-01 11:30:24 ..
-rw-r--r-- 644  1 boss boss 97708 2021-11-01 11:30:16 .custom
-rw-r--r-- 644  1 boss boss  4013 2021-10-11 22:04:04 .custom-dk

The above assumes your last column is always to be left-aligned, if that's not the case let us know as it's not hard to handle either way. It also assumes the alignment for a column can be determined by the values (numeric or not) in the fields in the last line of input.

If the input has to come from a pipe instead of a file (and so you can't open the input twice) then you can store the input in an array and print that in the END section:

$ cat tst.awk
BEGIN { FS = "\t" }
{
    for (i=1; i<=NF; i++) {
        width[i] = ( length($i) > width[i] ? length($i) : width[i] )
        align[i] = ( $i ~ /^[0-9]+$/ ? "" : "-" )
        vals[NR,i] = $i
    }
}
END {
    for (n=1; n<=NR; n++) {
        for (i=1; i<NF; i++) {
            printf "%" align[i] width[i] "s ", vals[n,i]
        }
        print vals[n,NF]
    }
}

and then call it as:

$ stat --printf="%A\t%a\t%h\t%U\t%G\t%s\t%.19y\t%n\n" . .* | awk -f tst.awk
1
  • This is amazing Ed, thanks. Indeed, I was mostly curious at how we we can rationalise outputs in a clean format like ls. I've used as a function for my .bashrc: lsec() { if [ -z "$@" ]; then args='.* *'; else args="$@"; fi; stat --printf="%A\t%a\t%h\t%U\t%G\t%s\t%.19y\t%n\n" $args | awk 'BEGIN { FS = "\t" }; { for (i=1; i<=NF; i++) { width[i] = ( length($i) > width[i] ? length($i) : width[i] ); align[i] = ( $i ~ /^[0-9]+ $/ ? "" : "-" ); vals[NR,i] = $i; } }; END { for (n=1; n<=NR; n++) { for (i=1; i<NF; i++) { printf "%" align[i] width[i] "s ", vals[n,i]; }; prin t vals[n,NF]; } }'; }
    – YorSubs
    Nov 1, 2021 at 17:35
1

You can use column -t your_file (but it doesn't get the right justified bit, as far as I can tell)

Outputs:

drwxr-xr-x  755  4   boss  boss  4096   2021-10-29  22:49:12  .
drwxr-xr-x  755  4   boss  boss  4096   2021-10-29  22:49:12  .
drwxr-xr-x  755  36  boss  boss  4096   2021-11-01  11:30:24  ..
-rw-r--r--  644  1   boss  boss  97708  2021-11-01  11:30:16  .custom
-rw-r--r--  644  1   boss  boss  4013   2021-10-11  22:04:04  .custom-dk
2
  • 1
    some column versions have the -R option for right alignment, but unfortunately the one in Ubuntu doesn't
    – phuclv
    Nov 1, 2021 at 12:33
  • That's great, and it's certainly "good enough" for most situations,, thanks, I never knew about column, very useful. I just double-checked a basic ls -AFhl there and I see that it does both space gaps and right-justifies columns that are sizes. It would be cool generically to see if we can process any output like that, though presumably that would be a more complex awk or sed type thing.
    – YorSubs
    Nov 1, 2021 at 14:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .