36

This ought to be really simple, but for some reason it is not working:

sed -i.bak -E 's/\t/  /' file.txt

Instead of replacing tab characters, it's replacing t characters. I've tried every variation on this I could think of, playing with quoting, etc. I've Googled and found everyone else using pretty similar expressions and they seem to work for them.

The -E is an OS X thing. I thought the failure might be a result of some weird quirk of OS X's sed, so I tried it with Ruby as well (without the -i), and got the same result:

ruby -pe '$_.gsub!(/\t/,"  ")' < file.txt > file.new

I'm using Bash 3.2.51 on OS X, and iTerm, although I can't see how any of those could be terribly relevant. I haven't set any weird environment variables, though I can post any that you think might be relevant.

What could be wrong?

UPDATE: I must have made some other mistake or typo when I tried the Ruby version, since Gilles points out that it does work (and I've never had him steer me wrong!). I'm not sure what happened, but I'm pretty sure it must have been my mistake.

  • 5
    May be you should try to replace the \t in the sed statement with CTRL-V<TAB> where <TAB> is the tab key and CTRL-V is control key and v pressed together. – unxnut Jul 18 '14 at 14:25
  • if ruby is also getting wrong answer, then it could be your regexp library. (I have tested both your commands, and both replace tab with 2 spaces.) It so then hopefully if you install Gnu sed it will also install the correct library. – ctrl-alt-delor Jul 18 '14 at 16:44
55

The syntax \t for a tab character in sed is not standard. That escape is a GNU sed extension. You find a lot of examples online that use it because a lot of people use GNU sed (it's the sed implementation on non-embedded Linux). But OS X sed, like other *BSD sed, doesn't support \t for tab and instead treats \t as meaning backslash followed by t.

There are many solutions, such as:

  • Use a literal tab character.

    sed -i.bak 's/  /  /' file.txt
    
  • Use tr or printf to produce a tab character.

    sed -i.bak "s/$(printf '\t')/  /" file.txt
    sed -i.bak "s/$(echo a | tr 'a' '\t')/  /" file.txt
    
  • Use bash's string syntax allowing backslash escapes.

    sed -i.bak $'s/\t/  /' file.txt
    
  • Use Perl, Python or Ruby. The Ruby snippet that you posted does work.

  • For sed scripts which are contained in a ...sed script (used via -f option), the literal tab characters seem the only possibility to me. When editing this with vim, set noexpandtab is important. – Tobias Jan 11 '18 at 11:46
  • Warning: Only use that "literal tab character" technique if you want your coworker to come back behind you and break your script later. Only use that tr technique if you want your coworker to stab you in the face when they read your script. – Bruno Bronosky Sep 6 '18 at 18:50
  • Is the second double-quotation mark misplaced in the second block of code? I had to move it to where the closing single-quote currently is. – Ellen Spertus Dec 24 '18 at 22:54
14

Use a Bash specific quoting which lets you use strings like in C, so that a real tab character is passed to sed, not an escape sequence:

sed -i.bak -E $'s/\t/  /' file.txt
  • 1
    Also called "ANSI-C" quoting if others want to look up more info about it. – wisbucky Jun 24 '16 at 23:28
  • 2
    Seems to work on any bourne shell, works on non-bash UNIXes as well. Doesn't work on csh-variants though. – jornane Nov 15 '16 at 14:43
1

As noted, not all sed implementations support the notation of \t as a horizontal tab.

You can easily achieve your substitution with:

 perl -pi.old -e 's{\t+}{ }g' file.txt

This performs an in situ replacment which preserves your original file as "*.old". Perl allows alternate delimiters for the classic / making the expression much more readable (i.e. devoid of the "leaning toothpick" syndrome).

The + says one or more repetitions of a tab character are to be replaced. The g modifier enables global replacements throughout the end of each line.

1
sed -i $'s/\t/  /g' file.txt 

works for me on OS X and is the same command i use on linux all the time.

  • Note that this replaces all tabs on every row whereas the OP intends to only replace the first (judging from the command they use). – Kusalananda Sep 26 '17 at 19:33
0

If you want a more powerful sed (supporting \t and more) than the one on OS X, install GNU sed.

  • Since it didn't work with Ruby either, I'm not sure why I would conclude that OS X's sed is the problem. Do you have a reason to believe that's the problem? I'd be happy to install GNU sed if I had reason to believe it would solve the problem, but it seems like I've pretty much ruled that out. – iconoclast Jul 18 '14 at 15:36
  • With Ruby, you'll have to use only one backslash: ruby -pe '$_.gsub!(/\t/," ")' < file.txt – vinc17 Jul 18 '14 at 15:45
0

You can also use echo inside sed:

sed -i "s/$(echo '\t')//g"

New contributor
saulR is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
  • Note that echo '\t' will just output \t in some shells' implementation of echo. – Kusalananda Jun 13 at 10:54
-1

I'm surprised no one suggested the very simple solution of: sed -i.bak -E 's/\\\t/ /' file.txt That should do the trick.

You need to escape the escape (hence the 3 \s) to allow sed to understand that you are trying to use a \t character in the regular expression when everything is substituted...

  • Why three backslashes specifically? – Michael Homer Jan 14 '16 at 0:19
  • 3
    If I use GNU sed, one \ is enough, as no escaping is necessary. The problem is that BSD sed does not support this syntax for tabs. – iconoclast Jan 16 '16 at 16:43
  • Does not work on my El Capitan. – Franklin Yu Jun 19 '16 at 6:46
-4

This worked for me.

sed -e 's/[\t]/ /g'

  • 3
    This is because you use GNU sed. This is not what the OP uses. – Kusalananda Sep 26 '17 at 19:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.