Why is sed not recognizing \t as a tab?

前端 未结 11 1544
伪装坚强ぢ
伪装坚强ぢ 2020-11-29 19:54
sed \"s/\\(.*\\)/\\t\\1/\" $filename > $sedTmpFile && mv $sedTmpFile $filename

I am expecting this sed script to insert a <

相关标签:
11条回答
  • 2020-11-29 20:32

    Using Bash you may insert a TAB character programmatically like so:

    TAB=$'\t' 
    echo 'line' | sed "s/.*/${TAB}&/g" 
    echo 'line' | sed 's/.*/'"${TAB}"'&/g'   # use of Bash string concatenation
    
    0 讨论(0)
  • 2020-11-29 20:33

    Instead of BSD sed, i use perl:

    ct@MBA45:~$ python -c "print('\t\t\thi')" |perl -0777pe "s/\t/ /g"
       hi
    
    0 讨论(0)
  • 2020-11-29 20:40

    @sedit was on the right path, but it's a bit awkward to define a variable.

    Solution (bash specific)

    The way to do this in bash is to put a dollar sign in front of your single quoted string.

    $ echo -e '1\n2\n3'
    1
    2
    3
    
    $ echo -e '1\n2\n3' | sed 's/.*/\t&/g'
    t1
    t2
    t3
    
    $ echo -e '1\n2\n3' | sed $'s/.*/\t&/g'
        1
        2
        3
    

    If your string needs to include variable expansion, you can put quoted strings together like so:

    $ timestamp=$(date +%s)
    $ echo -e '1\n2\n3' | sed "s/.*/$timestamp"$'\t&/g'
    1491237958  1
    1491237958  2
    1491237958  3
    

    Explanation

    In bash $'string' causes "ANSI-C expansion". And that is what most of us expect when we use things like \t, \r, \n, etc. From: https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting

    Words of the form $'string' are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard. Backslash escape sequences, if present, are decoded...

    The expanded result is single-quoted, as if the dollar sign had not been present.

    Solution (if you must avoid bash)

    I personally think most efforts to avoid bash are silly because avoiding bashisms does NOT* make your code portable. (Your code will be less brittle if you shebang it to bash -eu than if you try to avoid bash and use sh [unless you are an absolute POSIX ninja].) But rather than have a religious argument about that, I'll just give you the BEST* answer.

    $ echo -e '1\n2\n3' | sed "s/.*/$(printf '\t')&/g"
        1
        2
        3
    

    * BEST answer? Yes, because one example of what most anti-bash shell scripters would do wrong in their code is use echo '\t' as in @robrecord's answer. That will work for GNU echo, but not BSD echo. That is explained by The Open Group at http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16 And this is an example of why trying to avoid bashisms usually fail.

    0 讨论(0)
  • 2020-11-29 20:40

    I've used something like this with a Bash shell on Ubuntu 12.04 (LTS):

    To append a new line with tab,second when first is matched:

    sed -i '/first/a \\t second' filename
    

    To replace first with tab,second:

    sed -i 's/first/\\t second/g' filename
    
    0 讨论(0)
  • 2020-11-29 20:40

    sed doesn't support \t, nor other escape sequences like \n for that matter. The only way I've found to do it was to actually insert the tab character in the script using sed.

    That said, you may want to consider using Perl or Python. Here's a short Python script I wrote that I use for all stream regex'ing:

    #!/usr/bin/env python
    import sys
    import re
    
    def main(args):
      if len(args) < 2:
        print >> sys.stderr, 'Usage: <search-pattern> <replace-expr>'
        raise SystemExit
    
      p = re.compile(args[0], re.MULTILINE | re.DOTALL)
      s = sys.stdin.read()
      print p.sub(args[1], s),
    
    if __name__ == '__main__':
      main(sys.argv[1:])
    
    0 讨论(0)
  • 2020-11-29 20:40

    I think others have clarified this adequately for other approaches (sed, AWK, etc.). However, my bash-specific answers (tested on macOS High Sierra and CentOS 6/7) follow.

    1) If OP wanted to use a search-and-replace method similar to what they originally proposed, then I would suggest using perl for this, as follows. Notes: backslashes before parentheses for regex shouldn't be necessary, and this code line reflects how $1 is better to use than \1 with perl substitution operator (e.g. per Perl 5 documentation).

    perl -pe 's/(.*)/\t$1/' $filename > $sedTmpFile && mv $sedTmpFile $filename
    

    2) However, as pointed out by ghostdog74, since the desired operation is actually to simply add a tab at the start of each line before changing the tmp file to the input/target file ($filename), I would recommend perl again but with the following modification(s):

    perl -pe 's/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
    ## OR
    perl -pe $'s/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
    

    3) Of course, the tmp file is superfluous, so it's better to just do everything 'in place' (adding -i flag) and simplify things to a more elegant one-liner with

    perl -i -pe $'s/^/\t/' $filename
    
    0 讨论(0)
提交回复
热议问题