【问题标题】:sed/awk replace in all matchessed/awk 替换所有匹配项
【发布时间】:2012-07-06 00:06:16
【问题描述】:

我想反转一堆文件中的所有颜色值。颜色都是十六进制格式#ff3300,因此可以使用 sed 命令按字符进行反转

y/0123456789abcdef/fedcba9876543210/

如何遍历所有颜色匹配并在 sed 或 awk 中进行字符转换?

编辑:

样本输入:

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

想要的输出:

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

【问题讨论】:

    标签: regex sed awk


    【解决方案1】:

    编辑:我根据您的编辑更改了回复。

    好的,sed 可能会导致处理困难。 awk 或多或少可以轻松完成这项任务,但我发现 perl 更容易完成这项任务:

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

    基本上你找到模式,然后执行右侧,它在匹配时执行 tr,并替换那里的值。

    【讨论】:

    • 对不起,我不清楚文件是什么样的,我编辑了问题帖
    • 记住这个版本对十六进制数字区分大小写。
    • @Jite,是的,就像原来的问题一样。
    • @DiegoSevilla - 是的,这并不是对您的解决方案产生负面影响,我只是想让 sceid 意识到它以防止将来出现问题 :)
    • 由于最后一个% 后面的“r”,我得到了bareword 和其他错误。如果我删除“r”(我在文档中没有找到),我会收到以下错误:“尝试修改只读值”。
    【解决方案2】:

    倒置实际上是一个减法。要反转十六进制,只需从 ffffff 中减去它。
    考虑到这一点,您可以构建一个简单的脚本来处理每一行,提取十六进制,反转它们,并将它们注入回行。


    这仅使用 Bash(参见数组、printf -v+= 等)(没有外部工具):

    #!/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"
    

    像这样使用它:

    $ ./invhex infile > outfile
    

    测试用例输入:

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

    处理后的输出:

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

    【讨论】:

      【解决方案3】:

      这可能对你有用(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
      

      解释:

      • /#[a-f0-9]\{6\}\&gt;/!b 排除不包含所需模式的行
      • s//\n&amp;/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//H 命令中删除换行符。

      【讨论】:

        【解决方案4】:

        这行得通……

        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}'
        

        第一个 sed 命令在十六进制代码周围插入换行符,然后可以在所有以散列开头的行上进行替换。可能有一个优雅的解决方案可以再次合并这些行,但是 awk 命令可以完成这项工作。唯一的假设是不会有两个直接紧随其后的十六进制代码。如果是这样,则必须修改此步骤。

        【讨论】:

        • 不需要将 sed 传递给 sed,因为您已经使用了 -e 标志,只需将其设为 sed -e 'expr' -e 'expr2'
        • 我试过了,但由于某种原因它不起作用...有什么解释吗?
        • 您很可能使用了一些错误的语法,因为它确实有效 :) 只是不要忘记在下一个表达式之前添加另一个 -e
        猜你喜欢
        • 2014-11-02
        • 2023-02-02
        • 1970-01-01
        • 2013-06-01
        • 1970-01-01
        • 2017-01-31
        • 1970-01-01
        • 2018-06-27
        • 2012-09-19
        相关资源
        最近更新 更多