代码之家  ›  专栏  ›  技术社区  ›  chtenb

如何在直线的匹配部分内执行sed变换

  •  2
  • chtenb  · 技术社区  · 10 年前

    在匹配特定模式的行内进行sed变换很容易,但如果我们只想变换行的某一部分,该怎么办?

    简单的示例

    假设我们想使所有行中的所有字符都大写 # 。我们可以使用以下形式的命令来完成。

    sed '/^#/ y/abcdef/ABCDEF/'
    

    假设我们只想将这些行中的第一个单词大写。我们如何使用sed翻译?

    更高级的应用程序

    我想在输出的图形部分中用反斜杠替换斜杠 git --no-pager log --all --graph --decorate --oneline --color=always | tac .

    之前

    | * | | 279e9ad (tag: v0.0.4.334, origin/DR) asdfasdf
    | | |/ /
    | |/| / /
    | | |/ / /
    | | |\ \ \
    | | * | |   1fc7ab7 (tag: v0.0.4.337) Merge branch 'DR' into NextMajor
    | | | * | d24e21d (tag: v0.0.4.341, origin/DR-01) DR-010728 Updated unit tests
    | | |\ \
    | | * |   8c01099 (tag: v0.0.4.338, tag: 0.0.4_MILESTONE_RELEASE) Merge 
    

    之后

    | * | | 279e9ad (tag: v0.0.4.334, origin/DR) asdfasdf
    | | |\ \
    | |\| \ \
    | | |\ \ \
    | | |/ / /
    | | * | |   1fc7ab7 (tag: v0.0.4.337) Merge branch 'DR' into NextMajor
    | | | * | d24e21d (tag: v0.0.4.341, origin/DR-01) DR-010728 Updated unit tests
    | | |/ /
    | | * |   8c01099 (tag: v0.0.4.338, tag: 0.0.4_MILESTONE_RELEASE) Merge 
    

    请注意,提交消息中的斜杠保持不变,但图形部分的斜杠被转换。

    4 回复  |  直到 10 年前
        1
  •  1
  •   Ed Morton    10 年前

    保持简单,只需使用awk。e、 g.使用GNU awk匹配第三个参数():

    $ cat tst.awk        
    {
        match($0,/([| *\/\\]+)(.*)/,a)
        gsub(/\//,RS,a[1])
        gsub(/\\/,"/",a[1])
        gsub(RS,"\\",a[1])
        print a[1] a[2]
    }
    
    $ awk -f tst.awk file
    | * | | 279e9ad (tag: v0.0.4.334, origin/DR) asdfasdf
    | | |\ \
    | |\| \ \
    | | |\ \ \
    | | |/ / /
    | | * | |   1fc7ab7 (tag: v0.0.4.337) Merge branch 'DR' into NextMajor
    | | | * | d24e21d (tag: v0.0.4.341, origin/DR-01) DR-010728 Updated unit tests
    | | |/ /
    | | * |   8c01099 (tag: v0.0.4.338, tag: 0.0.4_MILESTONE_RELEASE) Merge 
    

    添加了任何awk和注释,以防脚本不明显:

    $ cat tst.awk        
    {
        match($0,/[| *\/\\]+/)              # find the segment of text you want
        tgt = substr($0,RSTART,RLENGTH)     # save that segment in a variable tgt
        gsub(/\//,RS,tgt)                   # change all /s to newlines in tgt
        gsub(/\\/,"/",tgt)                  # change all \s to /s in tgt
        gsub(RS,"\\",tgt)                   # change all newlines to \s in tgt
        print tgt substr($0,RSTART+RLENGTH) # print tgt plus rest of the line
    }
    

    我们在字符交换期间使用换行符作为tmp值,因为这行中肯定不会出现换行符。

    将以开头的每行的第一个单词 # 顺便说一句,大写可能是:

    awk '/^#/{$1=toupper($1)}1' file
    

    或:

    awk '/^#/{$2=toupper($2)}1' file
    

    根据您的输入数据 word 、和空白要求。

    如果您想要匹配的文本可以包含控制字符,就像您的注释中那样,那么只需在正则表达式中允许它,例如:

        match($0,/([[:space:][:cntrl:]|*\/\\]+)(.*)/,a)
    
        2
  •  1
  •   ghoti    10 年前

    这里有一个简单的sed解决方案,应该是可移植的(即在除GNU之外的sed变体中工作)。这会交换不在字母后面的斜线(至少在示例数据中有效)。

    sed -e 's:\([^a-z]\)/:\1\\:g;t' -e 's:\([^a-z]\)\\:\1/:g' file
    

    其分解过程有点像这样:

    • s:\([^a-z]\)/:\1\\:g -用反斜杠替换正斜杠
    • t -如果我们只是做了一个替换,跳到最后(避免下一个替换)
    • s:\([^a-z]\)\\:\1/:g -用正斜杠替换反斜杠。

    将其一分为二的原因 -e 表达式是sed的一些变体要求分支名称位于脚本中一行的末尾。a的结尾 -电子 表达式被认为等同于一行的末尾。

        3
  •  0
  •   potong    10 年前

    这可能适用于您(GNU sed):

    sed '/^#/s/\w\+/\U&/' file
    

    或:

    sed '/^#/!b;s/\w\w*/&\n/;h;y/abcdef/ABCDEF/;G;s/\n.*\n//' file
    
        4
  •  0
  •   Tom Fenech    10 年前

    如果您的sed版本支持它,您可以使用 \U 要将文本转换为大写:

    sed -r 's/(^# *)([^ ]*)/\1\U\2/'
    

    这将捕获以开头的任何行的第一部分 # (包括可选空格),然后是下一个空格字符。第二个捕获组转换为大写。

    如果它不支持,那么您可以始终使用perl:

    perl -pe 's/(^#\s*)([\S]*)/$1\U$2/'
    

    我用过 \s \S 在该版本中,它们相当于 [[:space:]] (空格字符)和 [^[:space:]] (非空格字符)。根据您使用的文件的具体情况,您可能需要使用稍微不同的模式。