If you qualify word to mean any sequence of 1 or more non-blank characters then the answer is definitely yes, and it is very simply done as well. This is because [[:blank:]]*
and [^[:blank:]]*
are boolean complements and - provided all characters in a string are complete - [[:blank:]]*
U[^[:blank:]]*
can describe any possible string in much the same way .*
does.
If an incomplete character or otherwise invalid byte sequence exists within a string neither can successfully describe it head to tail - as can sometimes occur when interpreting a string in the wrong encoding. To ensure a complete character per byte in any string, the C locale can be forced like:
LC_ALL=C sed ...
...which would avoid any issues describing the string from head to tail with an all-inclusive pattern such as .*
or ([ ]*[^ ]*)*
A fully complementary pattern can repeat as many times as is necessary from left to right the length of any string to land on the last possible occurrence without any break in the pattern. This is, definitively, regular language.
BRE:
sed 's/\(\([^[:blank:]]*\)[[:blank:]]*\)*/\2/'
ERE:
sed -E 's/(([^[:blank:]]*)[[:blank:]]*)*/\2/'
Both of these versions will still print blank lines, and this is because the Kleene *
star matches zero or more occurrences of a pattern. It first matches zero or more not blank characters, then zero or more blank characters, then zero or more occurrences of the grouped matches until it has matched the string in its entirety.
Having matched all of this, the magic happens in the replacement - the references returned by groups \1
and \2
are the last occurrences of each. So when the replacement is made all of the string is replaced with only the last occurrence on a line of zero or more not blank characters - or the subgroup \2
.
Of course this works for any possible string - even an empty one - which means both forms will print newline characters for lines which contain only blank characters or none at all. To handle this there are a couple things you can do, but first let's make the character class a little easier to type:
b='[:blank:]'
Now, to print only if a line contains one or more not blank characters you can do:
BRE:
sed -n "s/\(\([^$b]*\)[$b]*\)*/\2/;/./p"
ERE:
sed -En "/[^$b]/s/(([^$b]*)[$b]*)*/\2/p"
- BRE case - the substitution is always performed and only pattern spaces with at least one remaining character are printed.
- ERE case - the substitution is only ever attempted on a pattern space containing at least one not blank char.
Either form will work with either method - as long as the syntax is correct.
The -n
switch disables auto-printing of pattern space, and the p
flag to the s///
ubstitution or the /
address/
commands prints its results only if successful.
This same logic can be applied to get any {num}
th occurrence, as well, like:
BRE:
sed -n "s/\([$b]*\([^$b]\{1,\}\)\)\{num\}.*/\2/p"
ERE:
sed -En "s/([$b]*([^$b]+)){num}.*/\2/p"
...where the num
in both regexps can be replaced with a number to print only the {num}
th specified occurrence of a sequence of not blank characters. A slightly different form is used here to ensure the count is not skewed for leading space in a string.
Note that the -E
ERE switch to sed
is supported in both BSD and GNU versions, though it is not yet POSIX standard syntax.
sed
?sed
command deletes the entire second text-containing line (a line that has trailing whitespace) using my admittedly ancient version ofsed
. MacOS:man sed
says "BSD May 10, 2005".