Use sed to reformat line, duplicate one section onto the begining of other sections on seperate lines

95 views Asked by At

For LS_COLORS and ~/.dir_colors loading with a filter (sed?) Rather than having "pattern color" "pattern color"... on 10 lines, I'd like to have DEF_COLOR color pattern pattern pattern... on one line. How can sed reformat a line so I can use

DEF_COLOR 31 .tar .tgz .zip .z .gz .bz .tbz .tbz

and get the many lines needed in the actual config file format? Like this...

.tar 31
.tgz 31
.zip 31
.z   31
.gz  31
.bz  31
.tbz 31
5

There are 5 answers

6
Ed Morton On BEST ANSWER

Is this what you're trying to do?

$ cat file
DEF_COLOR 31 .tar .tgz .zip .z .gz .bz .tbz .tbz

$ awk '{for (i=3;i<=NF;i++) print $i, $2}' file
.tar 31
.tgz 31
.zip 31
.z 31
.gz 31
.bz 31
.tbz 31
.tbz 31

If you only want to do that for the line that starts with DEF_COLOR (the only line in the example but maybe you have others) then:

awk '/^DEF_COLOR/{for (i=3;i<=NF;i++) print $i, $2}' file

The above will behave the same way using any awk in any shell on every Unix box so if you're looking for a solution that's portable across all Unix systems, this is it.

0
tshiono On

With GNU sed how about:

echo DEF_COLOR 31 .tar .tgz .zip .z .gz .bz .tbz .tbz21 .tz .zoo .7z .rz | sed 's/ /\n/g' | sed -n '
2{h;d}           ;# save the 2nd line in hold space and delete pettern space
3,${G;s/\n/ /;p} ;# append hold space to pattern space; replace the 1st newline with a whitespace; then print pattern space

Output:

.tar 31
.tgz 31
.zip 31
.z 31
.gz 31
.bz 31
.tbz 31
.tbz21 31
.tz 31
.zoo 31
.7z 31
.rz 31

As @stevesliva comments, I'd prefer to use other language for this kind of task :)

2
9mjb On
sed '/^DEF_COLOR */{ s///      ; t x ; :x
s/\([^ ]*\)  *\([^ ]*\)/\2 \1\nDEF_COLOR \1/; t ok
d
:ok ; P;D}
  1. Only work on DEF_COLOR lines, delete that and spaces, and use t x ; :x (nop) to clear the jump flag.
  2. Then s/// to swap the first 2 words (31 .tar -> .tar 31) for printing, but also add a newline (end printing) and add DEF_COLOR color again, so it's still a DEF_COLOR line after the \n.
    DEF_COLOR 31 .tar .tgz... becomes .tar 31\nDEF_COLOR 31 .tgz... and if that worked goto ok (skip d in #3)(t jumps if s/// worked, but it worked in step #1 too, that's why the t ok;:ok there, so the t here tests only if this s/// worked) (this step is ugly, I'm sure there are better ways)
  3. If s/// failed (just color remains, no patterns) delete it all. (line done)
  4. ok: Swapping pattern/color worked, so P(print to newline(pattern color));D(delete to newline) and start again (continue shortening the DEF_COLOR line till there are no more patterns)

I'm sure this can be improved, but it works.
PS: Sorry to answer my own question. (and I think I'll use the perl solution)

1
stevesliva On

I like sed, but I don't like it for this. (It doesn't do field separators well)

Here's a perl version.

perl -lne '@f=split;print "$_ $f[1]" for @f[2..$#f]'

  • split the line into array @f
  • use -l to get line break on every print
  • print every item in @f from the 3rd to the last with the 2nd item afterwards

Or, as @tshiono points out, add -a to get the array automatically:

perl -lane 'print "$_ $F[1]" for @F[2..$#F]'

In general perl -p and perl -n put perl into a stream processing mode analogous to sed and sed -n ... but with many more tools available.

0
9mjb On

Just making it a script seems the best answer.

function EXT() { for x in "$@"; do echo ".$x $COLOR"; done; }
COLOR=31 EXT tar tgz zip z gz bz tbz tbz

To answer the question as I asked it
DEF_COLOR 31 .tar .tgz .zip .z .gz .bz .tbz .tbz
I could use

function DEF_COLOR { C="$1"; shift; for x in "$@"; do echo "$x $C"; done; }