Match a string that contains a newline using sed

后端 未结 4 1245
灰色年华
灰色年华 2020-12-11 02:39

I have a string like this one:

    #
    pap

which basically translates to a \\t#\\n\\tpap and I want to replace it with:

相关标签:
4条回答
  • 2020-12-11 03:09

    A GNU sed solution that doesn't require reading the entire file at once:

    sed '/^\t#$/ {n;/^\tpap$/a\\tpython'$'\n''}' file
    
    • /^\t#$/ matches comment-only lines (matching \t# exactly), in which case (only) the entire {...} expression is executed:
      • n loads and prints the next line.
      • /^\tpap/ matches that next line against \tpap exactly.
      • in case of a match, a\\tpython will then output \n\tpython before the following line is read - note that the spliced-in newline ($'\n') is required to signal the end of the text passed to the a command (you can alternatively use multiple -e options).

    (As an aside: with BSD sed (OS X), it gets cumbersome, because

    • Control chars. such as \n and \t aren't directly supported and must be spliced in as ANSI C-quoted literals.
    • Leading whitespace is invariably stripped from the text argument to the a command, so a substitution approach must be used: s//&\'$'\n\t'python'/ replaces the pap line with itself plus the line to append:

      sed '/^'$'\t''#$/ {n; /^'$'\t''pap$/ s//&\'$'\n\t'python'/;}' file
      

    )


    An awk solution (POSIX-compliant) that also doesn't require reading the entire file at once:

    awk '{print} /^\t#$/ {f=1;next} f && /^\tpap$/ {print "\tpython"} {f=0}' file
    
    • {print}: prints every input line
    • /^\t#$/ {f=1;next}: sets flag f (for 'found') to 1 if a comment-only line (matching \t# exactly) is found and moves on to the next line.
    • f && /^\tpap$/ {print "\tpython"}: if a line is preceded by a comment line and matches \tpap exactly, outputs extra line \tpython.
    • {f=0}: resets the flag that indicates a comment-only line.
    0 讨论(0)
  • 2020-12-11 03:18

    This might work for you (GNU sed):

    sed '/^\t#$/{n;/^\tpap$/{p;s//\tpython/}}' file
    

    If a line contains only \t# print it, then if the next line contains only \tpap print it too, then replace that line with \tpython and print that.

    0 讨论(0)
  • 2020-12-11 03:21

    try this line with gawk:

    awk -v RS="\0" -v ORS="" '{gsub(/\t#\n\tpap/,"yourNEwString")}7' file
    

    if you want to let sed handle new lines, you have to read the whole file first:

    sed ':a;N;$!ba;s/\t#\n\tpap/NewString/g' file
    
    0 讨论(0)
  • 2020-12-11 03:30

    A couple of pure bash solutions:

    Concise, but somewhat fragile, using parameter expansion:

    in=$'\t#\n\tpap\n' # input string
    
    echo "${in/$'\t#\n\tpap\n'/$'\t#\n\tpap\n\tpython\n'}"
    
    • Parameter expansion only supports patterns (wildcard expressions) as search strings, which limits the matching abilities:
    • Here the assumption is made that pap is followed by \n, whereas no assumption is made about what precedes \t#, potentially resulting in false positives.
    • If the assumption could be made that \t#\n\tpap is always enclosed in \n, echo "${in/$'\n\t#\n\tpap\n'/$'\n\t#\n\tpap\n\tpython\n'}" would work robustly; otherwise, see below.

    Robust, but verbose, using the =~ operator for regex matching:

    The =~ operator supports extended regular expressions on the right-hand side and thus allows more flexible and robust matching:

    in=$'\t#\n\tpap' # input string 
    
    # Search string and string to append after.
    search=$'\t#\n\tpap'
    append=$'\n\tpython'
    
    out=$in # Initialize output string to input string.
    if [[ $in =~ ^(.*$'\n')?("$search")($'\n'.*)?$ ]]; then # perform regex matching
        out=${out/$search/$search$append} # replace match with match + appendage
    fi
    
    echo "$out"
    
    0 讨论(0)
提交回复
热议问题