3

I have some files that are indented by tabs or spaces or both. I want to convert leading tabs to spaces (one tab to 4 spaces). This includes tabs after some leading spaces. Below are some examples of some input line and the expected result.

+-----+---------------+-----------------+
| No. | Original line | Expected result |
+-----+---------------+-----------------+
|   1 | \t␣xxx        | ␣␣␣␣␣xxx       |
|   2 | ␣␣␣\txxx      | ␣␣␣␣␣␣␣xxx     |
|   3 | \t␣\txxx      | ␣␣␣␣␣␣␣␣␣xxx   |
|   4 | \tx\txx       | ␣␣␣␣x\txx       |
+-----+---------------+-----------------+

I can't use the expand command here, as it can't handle the cases when spaces and tabs are mixed. Below is an example.

user1@ubuntu$ printf "\t  \txxx" | od -t a
0000000  ht  sp  sp  ht   x   x   x
0000007
user1@ubuntu$ printf "\t  \txxx" | expand -i -t 4 | od -t a
0000000  sp  sp  sp  sp  sp  sp  sp  sp   x   x   x
0000013
user1@ubuntu$ 

As you can see, the two spaces in the original string were simply dropped. How to solve my problem? I've read other similar questions on this site, but they are not exactly the same as my problem.

2 Answers 2

2

You can use sed (I use cat -T to display the file because it shows tabs as ^I):

$ cat -T file
^I abc
^I  ^Ixde^Inot
$ sed ':x;s|^\( *\)\t|\1    |;tx' file | cat -T
     abc
          xde^Inot

Unfortunately, interpretation of \t as tab is a GNU sed extension and not POSIX specified. However, you can work around that using printf as Gilles shows here and write:

sed ":x;s|^\( *\)$(printf '\t')|\1    |;tx" file

What sed is doing there?

  • s|^\( *\)\t|\1 |

If sed finds a tab followed by zero or more space characters anchored to the line beginning, it substitutes the tab by 4 spaces. The pair \(\) delimits the matching group (which consists of zero or more preceding space characters) that is reproduced by \1.

  • tx

If a substitution was made, go to label :x. Otherwise, proceed to next line.

6
  • Thank you for the answer! This is the first time that I noticed sed's t command is so powerful! Commented May 13, 2020 at 12:11
  • 1
    @Justalearner You are welcome! I've added an explanation of what is happening. Yes, t is very useful for these simple cases (but if used many times it may be worth considering using another tool). Commented May 13, 2020 at 12:59
  • Do you have any other good use cases of sed's t or T command? Commented May 13, 2020 at 13:05
  • 1
    Thanks again! I've learn a lot from you:-) Commented May 13, 2020 at 13:15
  • 1
    This is a "simple" but great answer (simple in quotes because sed's t command is on the sophisticated side of scripting but simple nonetheless, as in short). I have been using ':x;s/^\(\t*\) /\1\t/;tx to perform the reverse operation (4 spaces to tab) Commented Feb 20, 2023 at 16:42
1

To replace all tabs that come before the first non-space character with 4 spaces, try:

perl -pe '/^(\s+)/; $k=$1; $k=~s/\t/    /g; s/^\s+/$k/'file > newfile

The script will first find all leading whitespace (spaces, tabs, and anything else) and save it as $k. It then replaces all tabs with 4 spaces in $k and finally replaces all leading whitespace with the current value of $k.

To limit to spaces and tabs only, you can do:

perl -pe '/^([ \t]+)/; $k=$1; $k=~s/\t/    /g; s/^\s+/$k/'file > newfile

Run on your example strings, these solutions produce:

$  printf '\t xxx\n   \txxx\n\t \txxx\n\tx\txx\n'  | perl -pe '/^(\s+)/; $k=$1; $k=~s/\t/    /g; s/^\s+/$k/' | od -t a
0000000  sp  sp  sp  sp  sp   x   x   x  nl  sp  sp  sp  sp  sp  sp  sp
0000020   x   x   x  nl  sp  sp  sp  sp  sp  sp  sp  sp  sp   x   x   x
0000040  nl  sp  sp  sp  sp   x  ht   x   x  nl
0000052
2
  • Thanks for the answer. This doesn't solve my problem. When I try printf "\t xxx" | sed "s/^[ \t]*/ /" | od -t a, I got sp sp sp sp x x x. What I want is sp sp sp sp sp x x x. Commented May 13, 2020 at 9:09
  • @Justalearner ah, I think I got it now. See updated answer. Commented May 13, 2020 at 9:22

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.