71

I have tried:

echo -e "egg\t  \t\t salad" | sed -E 's/[[:blank:]]+/\t/g'

Which results in:

eggtsalad

And...

echo -e "egg\t  \t\t salad" | sed -E 's/[[:blank:]]+/\\t/g'

Which results in:

egg\tsalad

What I would like:

egg salad
Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
Zach
  • 2,445
  • 3
  • 20
  • 25

6 Answers6

92

Try: Ctrl+V and then press Tab.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
yan
  • 20,644
  • 3
  • 38
  • 48
  • Also, make sure you quote your sed expression if you include literal whitespace in it, otherwise the shell gets confused. This tripped me up at first. – Bobby Jack Jul 02 '13 at 15:24
  • 2
    This sucks when posting code in a forum or whatever, we can't post a literal TAB character. – Sam Watkins Sep 03 '14 at 06:47
  • 2
    @dongiulio According to the readline(3) manpage, "C-V" is by default a quoted-insert, which means that it will "Add the next character that you type to the line verbatim." – Aissen Mar 23 '18 at 09:55
  • 1
    Why, Apple... WHY?! – Swivel Jun 13 '19 at 15:43
46

Use ANSI-C style quoting: $'string'

sed $'s/foo/\t/'

So in your example, simply add a $:

echo -e "egg\t  \t\t salad" | sed -E $'s/[[:blank:]]+/\t/g'
wisbucky
  • 33,218
  • 10
  • 150
  • 101
  • FWIW - if you add the $ using vim, in a 'sh' file with syntax on, will change the highlighting of the '\t' portion, so even vim knows the difference! – DryLabRebel Jun 14 '19 at 02:20
  • 1
    very useful, I agree this should be the answer but can someone explain why this magical $ works so fine? – Fırat Uyulur Aug 30 '19 at 09:09
26

OSX's sed only understands \t in the pattern, not in the replacement doesn't understand \t at all, since it's essentially the ancient 4.2BSD sed left over from 1982 or thenabouts. Use a literal tab (which in bash and vim is Ctrl+V, Tab), or install GNU coreutils to get a more reasonable sed.

geekosaur
  • 59,309
  • 11
  • 123
  • 114
  • Really? I couldn't get sed to understand \t in the pattern either. I used [[:blank:]] instead. Maybe I wasn't escaping it properly. -- Thanks. – Zach Mar 22 '11 at 22:18
  • Inside `"` quotes, the shell will have processed the backslash and `sed` won't see it. With `sed`, and regexes in general, `'` quoting is strongly preferred. – geekosaur Mar 22 '11 at 22:20
  • Any chance of a code sample? It's not really important other than to satisfy my curiosity. echo -e "egg\tsalad" | sed -E 's/\t/_/' doesn't seem to work, neither does echo -e "egg\tsalad" | sed -E 's/\\t/_/' – Zach Mar 22 '11 at 22:24
  • ... *sigh* Lemme edit that out. Just doublechecked; I thought FreeBSD/OSX had gotten a `sed` that was *slightly* smarter than the 4.2BSD one, but in fact the only character escape it supports is `\n`. – geekosaur Mar 22 '11 at 22:28
  • I have to admit, I get tripped by this kind of thing regularly because most of my experience is from the System III/V side of things and BSD is still somewhat foreign to me despite several years running FreeBSD and OS X. (And most of my `sed` usage is in Linux.) – geekosaur Mar 22 '11 at 22:32
  • Oh well, it works for what I need it for, but it is annoying that OS X can't handle \t,\s, etc. - Thanks anyway. – Zach Mar 22 '11 at 22:34
  • It's obviously not the ancient `sed` from 1982, because then it wouldn't understand `[[:blank:]]`. POSIX doesn't describe `\t` support in `sed`. Therefore, it would be nonconforming to support that. Converting `\t` to tab breaks shell scripts which rely on `sed` **not** to do that. – Kaz Jun 24 '16 at 23:33
17

Another option is to use $(printf '\t') to insert a tab, e.g.:

echo -e "egg\t  \t\t salad" | sed -E "s/[[:blank:]]+/$(printf '\t')/g"
robinst
  • 30,027
  • 10
  • 102
  • 108
  • 4
    This has the advantage of being more portable than `Ctrl+V Tab` since you can copy and paste the entire command and share it more easily without losing the control character. – beporter Feb 12 '16 at 14:49
1

try awk

echo -e "egg\t  \t\t salad" | awk '{gsub(/[[:blank:]]+/,"\t");print}'
kurumi
  • 25,121
  • 5
  • 44
  • 52
-1

A workaround for tab on osx is to use "\ ", an escape char followed by four spaces.

If you are trying to find the last instance of a pattern, say a " })};" and insert a file on a newline after that pattern, your sed command on osx would look like this:

sed -i '' -e $'/^\    \})};.*$/ r fileWithTextIWantToInsert' FileIWantToChange

The markup makes it unclear: the escape char must be followed by four spaces in order for sed to register a tab character on osx.

The same trick works if the pattern you want to find is preceded by two spaces, and I imagine it will work for finding a pattern preceded by any number of spaces as well.