【问题标题】:Sed to remove more than 2 words in a sentencesed删除句子中超过2个单词
【发布时间】:2021-10-27 13:46:19
【问题描述】:

我正在尝试获取 sed 命令,该命令将帮助我输出仅显示 2 个单词且不多于该单词的输出。

echo  "test1:pass,test2:fail,test3:pass,test4:pass,test5:pass,test6:pass asfas"  | sed 's/,/<br>/g; s/:/  #  /g; s/\b\(.\)/\u\1/g'

预期输出:

Test1  #  Pass
Test2  #  Fail
Test3  #  Pass
Test4  #  Pass
Test5  #  Pass
Test6  #  Pass 

我不希望 asfas 出现在最后的 Test6 行中。

另外,我只是希望结果应该是 PassFail,没有其他应该像 PAssPaSS 无论echo 命令中有什么PaSSPAssFaIlFAil,它都应该只替换为PassFailPassFail 之后提及的任何字词都应删除且无需显示。

有人可以告诉我从我写的内容中实现要求的更清洁的方法吗?

谢谢:)

【问题讨论】:

    标签: linux shell sed


    【解决方案1】:

    只需使用 awk。在每个 Unix 机器上的任何 shell 中使用任何 awk:

    $ echo  "test1:pass,test2:fail,test3:pass,test4:pass,test5:pass,test6:pass asfas" |
    awk -v RS=',' -F':' -v OFS=' # ' '
        {
            sub(/ .*/,"")
            for (i=1; i<=NF; i++) {
                $i = toupper(substr($i,1,1)) tolower(substr($i,2))
            }
            print
        }
    '
    Test1 # Pass
    Test2 # Fail
    Test3 # Pass
    Test4 # Pass
    Test5 # Pass
    Test6 # Pass
    

    【讨论】:

    • fOO:paSS 应该转换成fOO # Pass
    • @WalterA 修复了 paSS 的情况,谢谢。 fOO 不应该保持不变,因为 OP 在他们的示例中显示 test 变为 Test 所以即使 OP 没有说/显示第二个和后续字符应该发生什么,我也将它用驼峰形式表示第一个字符串,因为我只是假设他们希望在这方面与第二个字符串相同。
    【解决方案2】:

    在您的解决方案中,您应该使用\n,而不是&lt;BR&gt;,并调用sed 两次。
    还有一个小改动来删除该行的其余部分。

    echo "fOO:paSS,tesT2:fail,TESt:pasS,fdfdhfd:pass,test5:anyresult test,test6:pass asfas"|
      sed -r 's/,/\n/g' | sed -r 's/(.*):(.)(\w*).*/\1 # \u\2\L\3<br>/g'
    

    编辑:

    1. 我最初以为只会解析一个四个字母的单词。我更改了解决方案,所以它会保留第一个单词。
    2. OP 希望将其用于 HTML。我更喜欢&lt;pre&gt;...&lt;/pre&gt; 上面的解析文本,但我在每行末尾添加了一个&lt;br&gt;

    【讨论】:

    • 实际上我正在将结果以 html 格式发送到我需要它的地方。你能解释一下你的 sed 命令是如何处理输出的吗?我用这个 echo 命令试过了。 echo "fOO:paSS,tesT2:fail,TESt:pasS,fdfdhfd:pass,test5:anyresult test,test6:pass asfas" | sed -r 's/,/\n/g' | sed -r 's/(.*):(.)(...).*/\1 # \u\2\L\3/g' 它给出的输出为fOO # Pass tesT2 # Fail TESt # Pass fdfdhfd # Pass test5 # Anyr test6 # Pass 我想要的是“Anyresult”这个词不应该剩下一半,它应该显示完整的词。你能帮我实现吗?
    • 在 Bash 中,换行符是 `\n, not
      `。如果您需要 HTML 中断,请将它们添加到每行的末尾。
    • 但是任何结果转换为 Anyr 呢?
    • 我也改用\w*,而不是...
    • 实际上,这也按预期工作:echo "Foo:PaSS,fOO:paSS,tesT2:fail,TESt:pasS,fdfdhfd:pass,test5:anyresulttest,test6:pass asfas, foo7:fail,fooo9:fail " | sed -E 's/:/#/g;s/,/ /g;s/(.\S*)(#)(.)(\S*)\s/\1 \2 \U\3\L\4\n&lt;br&gt;/g;' | awk 'NF &lt; 2 || NF &gt; 3 { $1=""}1' | sed 's/\s//' 但这是正确的方法吗?
    【解决方案3】:

    这可能对你有用(GNU sed):

    sed 's/.*/\L&/;s/\w\+/\u&/g;s/:/ # /g;y/,/\n/' file | 
    sed 's/\w\+/&\n/2;P;d'
    

    两次调用 sed。

    第一次调用:

    • 全部小写。
    • 每个单词的第一个字符大写。
    • : 格式化为 #
    • 用逗号将行拆分为行。

    第二次调用:

    • 在行的第二个单词后用换行符分割行。
    • 仅打印两行中的第一行并删除另一行。

    注意如果不需要空白和单个字线,则可以改进第二次调用:

    sed -E 's/\w+/&\n/2;Ta;P;:a;d'
    

    【讨论】:

    • 嗨,我现在想我不想将所有内容都更改为小写。我希望按原样给出“测试”输入……在回显命令中……但通过失败应该只在“通过”和“失败”中可见,但测试可以是任何东西……正如我们将要做的那样有不同的测试,他们可以有任何名字......我怎样才能做到这一点?你能帮帮我吗?
    • 我正在这样做:echo "fOO:pass,tesT2:fail,TEST:pass,fdfdhfd:pass,test5: anyresult,test6:pass asfas " | sed 's/^:.*/\L&amp;/;s/\w\+/\u&amp;/g;s/:/ # /g;y/,/\n/' | sed 's/\w\+/&amp;\n/2;P;d' 是正确的方法吗?
    【解决方案4】:

    使用更复杂的输入(注意 test3 中不需要的文本包含逗号):

    test1:PAss,test2:FAil,test3:pass foobar, barfoo,test4:pass,test42:pass,test6:pass asfas
    

    我会调用 3 次 sed 和 1 次 cut。第一次调用将它分成几行,第二个进行必要的更改,最后一个用&lt;br&gt; 加入行:

    echo  "test1:PAss,test2:FAil,test3:pass foobar, barfoo,test4:pass,test42:pass,test6:pass asfas" |
        sed -e 's/,/\n/g' |
        sed -e '/^test[0-9]/ ! d' \
            -e 's/pass/Pass/i' \
            -e 's/fail/Fail/i' \
            -e 's/:/ # /' |
        cut -d' ' -f 1-3 |
        sed ':a; N; $!ba; s/\n/<br>/g'
    

    或者如果要求只使用sed:

    echo  "test1:PAss,test2:FAil,test3:pass foobar, barfoo,test4:pass,test42:pass,test6:pass asfas" |
        sed -e 's/,/\n/g' |
        sed -e '/^test[0-9]/ ! d' \
            -e 's/pass/Pass/i' \
            -e 's/fail/Fail/i' \
            -e 's/:/ # /' \
            -e 's/\([[:alnum:]]* # [[:alnum:]]*\).*/\1/' |
        sed ':a; N; $!ba; s/\n/<br>/g'
    

    两种情况下的输出:

    test1 # Pass<br>test2 # Fail<br>test3 # Pass<br>test4 # Pass<br>test42 # Pass<br>test6 # Pass
    

    并且没有代码格式化:

    test1 # Pass
    test2 # Fail
    test3 # Pass
    test4 # Pass
    test42 # Pass
    test6 # Pass

    • /^test[0-9]/ ! d 删除不以 test[0-9] 开头的行。
    • s/pass/Pass/i 不区分大小写,因此它匹配任何“通过”并将其替换为“通过”。因此为“失败”。
    • s/\([[:alnum:]]* # [[:alnum:]]*\).*/\1/ 捕获由 # 分隔的 2 个单词,并将整行替换为此捕获的内容。
    • :a; N; $!ba; s/\n/&lt;br&gt;/g 取自 https://www.baeldung.com/linux/join-multiple-lines#sed。它定义标签a,将行追加到模式空间,最后将\n 替换为&lt;br&gt;

    【讨论】:

    • 你能告诉我为什么你在第一种情况下更喜欢 cut 吗?
    • 嗨。非常感谢。这真的很有帮助.. 实际上 test 可以是任何东西.. 我们只需要任何一个单词名称即可。它可以是后面没有数字的任何东西。我只是想确保它的第一个字母是大写的。我怎样才能做到这一点?所以基本上字符串应该给出 Test # pass Test # Fail 的输出,仅此而已。 .
    • 我喜欢cut,因为它更短,对我来说更易读。
    • 要大写测试名称,您可以在该中间添加另一个表达式 sed 调用:-e 's/^\(.\)/\u\1/'。但它需要 GNU sed,因为 \u(参见 another question
    • 实际上我尝试在这样的脚本中添加相同的命令:echo -e $message | sed -e 's/,/\n/g' | sed -e 's/pass/Pass/i; s/fail/Fail/i; s/:/ # /; s/\([[:alnum:]]* # [[:alnum:]]*\).*/\1/' | sed ':a; N; $!ba; s/\n/&lt;br&gt;/g',但它没有按预期工作。我尝试以您提供的格式添加它,然后它也没有按预期工作,它没有显示通过或失败字符串..只是测试。然后我尝试将其转换为单行,但看起来它在脚本中没有以这种方式工作。我做错了吗?
    【解决方案5】:

    以下是shell命令:

    $ echo "test1:pass,test2:fail,test3:pass,test4:pass,test5:pass,test6:pass asfas" | sed '
       # replace test[0-9]:(pass or fail) by test[0-9] # (pass or fail).
       # match anything up until an optional comma after, to remove any text after
       # matched globally, so it repeats for each pattern
       s/\(test[0-9]\):\(pass\|fail\)[^,]*,\?/\1 # \2\n/g;
       # apply uppercase to first letters
       s/pass/Pass/gi; s/fail/Fail/gi;
       # The first pattern will add a trailing newline to pattern space
       # remove it
       s/\n*$//
    '
    

    会输出:

    test1 # Pass
    test2 # Fail
    test3 # Pass
    test4 # Pass
    test5 # Pass
    test6 # Pass
    

    您可以通过 regex crossowrds 愉快地学习 regex。

    【讨论】:

    • 嗨,@KamilCuk 我实际上不想硬编码这个词 test 我希望它是测试的任何名称..它不需要包含名称或数字.只是要求它应该是一个单词,如 Test1 或 Test.唯一的事情是,我希望第一个字母大写。休息可以是给定的输入。它不应该像:Test 2 或 Test Test2。你能在我只需要一个词的地方指导我吗?
    • regexcrossword.com , grymoire.com/Unix/Sed.html#uh-1 , sed manual I want the first letter to be capital必须被sed吗?使用 awk 听起来更容易。无论如何,使用\U GNU sed extension (或使用y 命令和一些保持/模式空间改组)可能很容易。
    • 我自己无法做到这一点@KamilCuk。你能告诉我如何在我可以在脚本中使用的单行 sed 命令中实现它吗?这就是我目前正在使用的强制输入 test(0-9) 我希望输入是任何东西来代替 test 只是第一个字母应该是大写。一切都应该保持原样。 echo "foo:pass,test2:fail,test3:pass,word3:pass,test5:pass,test6:pass asfas" | sed 's/\(test[0-9]\):\(pass\|fail\)[^,]*,\?/\1 # \2\n/g;s/pass/Pass/gi; s/fail/Fail/gi;s/\n*$//'
    • 所以不是test 匹配[^:]*\w*just the first letter should 要记住反向引用中的内容并应用 \u,如 sed 's/\(\w\+\)/\u\1/',请参阅手册。
    • 操作。我不知道为什么,但这让我感到非常困惑。我试着这样做:echo "test1:pass,test2:fail,test3:pass,test4:pass,test5:pass,test6:pass asfas" | sed 's/\(\w\+\)/\u\1/:\(pass\|fail\)[^,]*,\?/\1 # \2\n/g;s/pass/Pass/gi; s/fail/Fail/gi;s/\n*$//' 但它失败并说sed: -e expression #1, char 17: unknown option to s'`
    猜你喜欢
    • 2022-07-01
    • 2017-06-13
    • 2013-03-29
    • 1970-01-01
    • 2021-08-20
    • 1970-01-01
    • 1970-01-01
    • 2021-10-31
    • 1970-01-01
    相关资源
    最近更新 更多