I've been trying to use sed for aggregating a specific string pattern within a text for hours, but can't find the solution. I hope you guys know how to do it!?

  • The text subject to my Goal consists of string characters only (No invisible \t present).
  • I want to find string parts which contain (TAB), but at least two next to each other and maximum 8 adjacent to each other and replace them by a single (TAB) entry.

  • The search shall be performed within a Shell file, containing a bash script



text="Column One(TAB)(TAB)(TAB)Column Two(TAB)(TAB)Column three(TAB)Column4"

modText=`echo $text | sed 's/([(]\{1\}TAB[)]\{1\})\{2,8\}/(TAB)/g'`
  • I have tried several Versions of the sed-command, the one above is just one of them. My original idea was modText=`echo $text | sed 's/\(TAB\)\{1\})\{2,8\}/(TAB)/g'`

Would be really great if you could help me out. Got the Feeling that the solution is not so far away but just don't have any more ideas and my research didn't do it :-S

Text Example

"Column One(TAB)(TAB)(TAB)Column Two(TAB)(TAB)Column three(TAB)Column4"

My search criteria would be matched by the first two (TAB) Groups, occurring between "Column One" and "Column three".

The result shall look like this:

"Column One(TAB)Column Two(TAB)Column three(TAB)Column4"

  • No, it's a real issue for me, but I tried to formulate it clean and simple. I am actually working on a Shell script that will reformat entire files. The handling of this is just one issue in the way to get it done. – Silverlight Jul 19 '16 at 6:36

I'm not sure I understand how the "maximum 8" clause is supposed to apply, but the naive approach would be something like this:

sed 's/\((TAB)\)\{2,8\}/(TAB)/g'
  • The condition of max. 8 came from the idea that a series of (TAB) stands for a different type of formatting and therefore should be handled differently. – Silverlight Jul 19 '16 at 6:45
  • just tried your Approach and it works on Shell Level (not in my bash script). Maybe it's because I've been working with a function. – Silverlight Jul 19 '16 at 6:47
  • function reduceMultitabToSingle(){ eval param1="$1" retval=echo $param1 | sed 's/((TAB)\{2,8\}/(TAB)/g'` echo "reducing TABS to single:"$retval nline=$retval }` – Silverlight Jul 19 '16 at 6:47
  • 1
    Then you can do this: sed 's/\((TAB)\)\{8\}/(TAB8)/g; s/\((TAB)\)\{2,7\}/(TAB)/g – Satō Katsura Jul 19 '16 at 6:47
  • @Silverlight, you don't want to use eval there, that makes no sense and is dangerous. Use $(...) instead of `...`. The normal Bourne and portable function definition syntax is foo() { ..., not function foo() {... – Stéphane Chazelas Jul 19 '16 at 6:51

If you mean that the replacement should not be done if there are more than 8 adjacent (TAB)s, you could do:

sed '
   s/_/_u/g; # escape _
   s/|/_p/g; # escape |
   s/(TAB)/|/g; # use a single character in place of (TAB)
   s/.*/<&>/; # add leading and trailing non-| character
   s/\([^|]\)|\{2,8\}\([^|]\)/\1|\2/; # replace up to 8 | provided
                                      # they are not preceded nor followed
                                      # by |
   s/.\(.*\)./\1/; # undo wrapping
   s/|/(TAB)/g;    # undo replacement
   s/_p/|/g;s/_u/_/g; # undo escaping'

If your sed (like ssed) supports perl-like regexps, you can use look-around operators:

ssed -R 's/(?<!\(TAB\))(\(TAB\)){2,8}(?!\(TAB\))/(TAB)/g'

Or use perl directly:

perl -lpe 's/(?<!\(TAB\))(\(TAB\)){2,8}(?!\(TAB\))/(TAB)/g'

AT&T (ast-open) sed, with the -A/-X option supports a kind of extended regular expressions they call augmented that have a negation operator (x!) and conjunction operator (x&y). There, (.{5}&(\(TAB\))!) matches a sequence of 5 characters that is not (TAB). So with that sed, you can do something like:

sed -A '
  • Is ssed still around? The last version I'm aware of was released some ~10 years ago. The addition of Perl regexps was a nice idea but as I remember it conflicted with various system scripts with embedded \<mumble>, so it was eventually abandoned. I think the author moved to work on GNU sed. – Satō Katsura Jul 19 '16 at 7:04
  • Not sure, the System I have to work with does not recognize the command – Silverlight Jul 19 '16 at 7:10
  • Suppose reverted replacement can simplify sed script: replace >8 (TAB)s for some symbols, then replace 2-7 (TABS)s, then back 8's+ s/\((TAB)\)\{8\}/\a\a\a\a\a\a\a\a/g;:a;s/\a(TAB)/\a\a/g;ta;s/(TAB)(TAB)\+/(TAB)/g;s/\a/(TAB)/g – Costas Jul 19 '16 at 7:30
  • @SatoKatsura, it doesn't seem to have changed much if at all in the last 10 years but it's still available as a package on Debian. – Stéphane Chazelas Jul 19 '16 at 11:16

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.