Is it possible to replace only "leading tabs" with a set number of spaces per tab? I'm defining a leading tab as one that is only preceded by tabs or spaces. I'm defining a leading or an indentation tab as one that is only preceded by whitespace.

I have the following line in my .nexrc, cribbed from this one. It runs the lines between the lines containing the mark a and the mark b through a filter. that replaces tabs.

map \2 'a!'b pr -te2

However, it replaces non-leading tabs with two spaces well, which is not ideal since a literal tab is sometimes needed.

Is there an easy and portable way to replace leading tabs only with a set number of spaces per tab?

The following perl script (which could be collapsed to a one-liner) does the job of replacing leading tabs with a width supplied on the command line, but it relies on the availability of Perl and takes quadratic time (if the file consists of nothing but tabs).

#!/usr/bin/env perl

use strict;
use warnings;

my ($width) = @ARGV;
$width //= 2;
my $replacement = ' ' x $width;

while (<>) {
    while (s/^(\s*)\t/$1$replacement/g) { }

Here's an example of what I want to happen. ^I refers to a tab byte and # is a zero-width marker for the start of a line. The line below is a byte-ruler.


#^I^I ^Ia^Ib
# 1 23 45 67

AFTER (tab width of 2)

#       a^Ib
#12345678 90

AFTER (tab width of 4)

#             a^Ib
#12345678901234 56
  • 2
    Are you looking for an nvi solution, or a general command-line solution?
    – Sparhawk
    Mar 28 '19 at 7:16
  • 1
    expand (from GNU coreutils) would seem to be the obvious generic solution e.g. expand -it3 to replace each initial tab with 3 spaces Mar 28 '19 at 11:03
  • sed 's/^\t\t*/ /' replace leading tabs with 2 spaces
    – ctac_
    Mar 28 '19 at 15:53
  • sorry, I meant replacing leading tabs with two spaces per tab. Mar 28 '19 at 15:59
  • @GregoryNisbet If you want i read it add @ before my name. You can try sed ':A;s/(^\t)(\t*)/\2 /;tA'
    – ctac_
    Mar 28 '19 at 16:17

If you are looking for a generic solution using other command-line tools, one could use Awk for this.

echo "$var" | awk '{ gsub(/^[\t]+/," ",$0) }1'

The command above replaces one more more occurrences of Tab with a single space i.e. 2nd argument in gsub. You can make it configurable though by generating how many spaces you want

awk -v n=5 ' BEGIN{ spaces = sprintf("%"n"s"," ") }{ gsub(/^[\t]+/,spaces,$0) }1'

From the last update, it seems OP wants to replace the number of leading tabs with the exact number of spaces found,

awk -F$'\t' '{ spaces = sprintf("%"(NF-1)"s"," "); gsub(/^[\t]+/,spaces,$0) }1'

The logic is with setting field separator to \t, the number of leading tabs can be identified by doing NF-1. With the number of leading tabs identified, use it to generate the same of number of spaces from earlier sprintf() method.

You can see it by verifying the hexdump -C before and after the replacement

echo "$var" | hexdump -C
00000000  09 09 09 6e 65 77 63 6f  6e 74 65 6e 74 0a        |...newcontent.|

now with the replacement

echo "$var" | awk -F$'\t' '{ spaces = sprintf("%"(NF-1)"s"," "); gsub(/^[\t]+/,spaces,$0) }1' | hexdump -C
00000000  20 20 20 6e 65 77 63 6f  6e 74 65 6e 74 0a        |   newcontent.|

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.