1

I have a text like:

    it's like a foo.
     it's like a bar.
        it's like a bar.
  it's like a foo.

I want to remove the indentation or space at the first line and remove the same count of indentation or space in the following lines.

If the following lines space or indentation is less than the count of space or indentation in first line, then remove the all space or indentation at the beginning.

The output is:

it's like a foo.
 it's like a bar.
    it's like a bar.
it's like a foo.
1
  • 1
    What do you mean "space or indentation"? If the first line indentation consists of a space and a tab, what should happen with another line with, say, 3 leading spaces?
    – Quasímodo
    Jun 3, 2020 at 16:00

4 Answers 4

4

With expand (to expand TABs into SPCs assuming tab stops every 8 columns; other whitespace characters are not considered) and awk:

<your-file expand | awk -F '[^ ]' '
  NR == 1 {n = length($1)}
  {sub("^ {1,"n"}", ""); print}'
4

Using perl:

$ perl -pe 'if($.==1){s/^(\s+)//; $s=length($1);}else{s/^\s{0,$s}//}' file
it's like a foo.
 it's like a bar.
    it's like a bar.
it's like a foo.

To edit the original file directly, use -i:

perl -i -pe 'if($.==1){s/^(\s+)//; $s=length($1);}else{s/^\s{0,$s}//}' file
4
  • That doesn't give the correct output for the last line. Jun 3, 2020 at 17:25
  • @StéphaneChazelas whoops, indeed. I hadn't noticed the second requirement. Fixed now, thanks.
    – terdon
    Jun 3, 2020 at 17:34
  • perl -lpe 'if($.<2){/^(\s*)/;$s=length $1}s/^\s{0,$s}//' is similar, but handles some edge cases. IRL, I would add some spaces: perl -lpe ' if ($. == 1) { /^(\s*)/; $len = length $1 } s/^\s{0,$len}// '
    – jrw32982
    Jun 4, 2020 at 23:28
  • @jrw32982supportsMonicar what edge cases? As far as I can tell the only difference is that you also allow 0 spaces which doesn't seem very useful. You are also using -l which makes no difference appart from maybe slowing it down imperceptibly. I don't see what edge cases it could catch, what am I missing?
    – terdon
    Jun 5, 2020 at 8:59
2

Feels bad to have to start a python interpreter, but it's also an option:

python -c "import textwrap,sys; print(textwrap.dedent(sys.stdin.read()))" < myfile
1

You could do it with sed, with a little prep-work:

n=$(( $(sed -n '1s/[^[:space:]].*//; p; q' file | wc -c) - 1 ))
sed 's/^[[:space:]]\{1,'"$n"'\}//' file

The first line sets a variable ($n) to the number of leading whitespaces in the first line of the file. The number of bytes that wc sees includes a newline, so we subtract one for that. The second line runs sed with an expression that deletes whitespace from the beginning of the line in a variable amount -- from 1 through $n -- with the \{n,m\} syntax.

Your sed might support an "in-place" edit option (-i).

1
  • 1
    Note that wc -c counts the number of bytes, not the number of characters, let alone their display width. With GNU wc, you can use wc -L to get the display width of that first line. Jun 3, 2020 at 17:27

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.