sceid
sceid

Reputation: 165

sed/awk replace in all matches

I want to invert all the color values in a bunch of files. The colors are all in the hex format #ff3300 so the inversion could be done characterwise with the sed command

y/0123456789abcdef/fedcba9876543210/

How can I loop through all the color matches and do the char translation in sed or awk?

EDIT:

sample input:

random text... #ffffff_random_text_#000000__
asdf#00ff00
asdfghj

desired output:

random text... #000000_random_text_#ffffff__
asdf#ff00ff
asdfghj

Upvotes: 1

Views: 705

Answers (4)

potong
potong

Reputation: 58361

This might work for you (GNU sed):

sed '/#[a-f0-9]\{6\}\>/!b
s//\n&/g
h
s/[^\n]*\(\n.\{7\}\)[^\n]*/\1/g
y/0123456789abcdef/fedcba9876543210/
H
g
:a;s/\n.\{7\}\(.*\n\)\n\(.\{7\}\)/\2\1/;ta
s/\n//' file

Explanation:

  • /#[a-f0-9]\{6\}\>/!b bail out on lines not containing the required pattern
  • s//\n&/g prepend every pattern with a newline
  • h copy this to the hold space
  • s/[^\n]*\(\n.\{7\}\)[^\n]*/\1/g delete everything but the required pattern(s)
  • y/0123456789abcdef/fedcba9876543210/ transform the pattern(s)
  • H append the new pattern(s) to the hold space
  • g overwrite the pattern space with the contents of the hold space
  • :a;s/\n.\{7\}\(.*\n\)\n\(.\{7\}\)/\2\1/;ta replace the old pattern(s) with the new.
  • s/\n// remove the newline artifact from the H command.

Upvotes: 2

c00kiemon5ter
c00kiemon5ter

Reputation: 17594

The inversion is really a subtraction. To invert a hex, you just subtract it from ffffff.
With this in mind, you can build a simple script to process each line, extract hexes, invert them, and inject them back to the line.


This is using Bash (see arrays, printf -v, += etc) only (no external tools there):

#!/usr/bin/env bash

[[ -f $1 ]] || { printf "error: cannot find file: %s\n" "$1" >&2; exit 1; }

while read -r; do
    # split line with '#' as separator
    IFS='#' toks=( $REPLY )
    for tok in "${toks[@]}"; do
        # extract hex
        read -n6 hex <<< "$tok"
        # is it really a hex ?
        if [[ $hex =~ [0-9a-fA-F]{6} ]]; then
            # compute inversion
            inv="$((16#ffffff - 16#$hex))"
            # zero pad the result
            printf -v inv "%06x" "$inv"
            # replace hex with inv
            tok="${tok/$hex/$inv}"
        fi
        # build the modified line
        line+="#$tok"
    done
    # print the modified line and clean it for reuse
    printf "%s\n" "${line#\#}"
    unset line
done < "$1"

use it like:

$ ./invhex infile > outfile

test case input:

random text... #ffffff_random_text_#000000__
asdf#00ff00
bdf#cvb_foo
asdfghj
#bdfg

processed output:

random text... #000000_random_text_#ffffff__
asdf#ff00ff
bdf#cvb_foo
asdfghj
#bdfg

Upvotes: 2

Diego Sevilla
Diego Sevilla

Reputation: 29011

EDIT: I changed my response as per your edit.

OK, sed may result in a difficult processing. awk could do the trick more or less easily, but I find perl much more easy for this task:

$ perl -pe 's/#[0-9a-f]+/$&=~tr%0123456789abcdef%fedcba9876543210%r/ge' <infile >outfile

Basically you find the pattern, then execute the right-hand side, which executes the tr on the match, and substitutes the value there.

Upvotes: 2

Karl Nordstr&#246;m
Karl Nordstr&#246;m

Reputation: 327

This works...

cat test.txt |sed -e 's/\#\([0123456789abcdef]\{6\}\)/\n\#\1\n/g' |sed -e ' /^#.*/ y/0123456789abcdef/fedcba9876543210/' | awk '{lastType=type;type= substr($0,1,1)=="#";} type==lastType && length(line)>0 {print line;line=$0} type!=lastType {line=line$0} length(line)==0 {line=$0} END {print line}'

The first sed command inserts line breaks around the hex codes, then it is possible to make the substitution on all lines starting with a hash. There are probably an elegant solution to merge the lines back again, but the awk command does the job. The only assumption there is that there won't be two hex-codes following directly after each other. If so, this step has to be revised.

Upvotes: 1

Related Questions