Um BRE fazendo o que você está tentando em sed
pode parecer:
sed 's/ *\(\([^ ]*\) *\)\{[num]\}.*//'
... ou como um ERE para os sed
s que o suportam, como as versões GNU e BSD:
sed -E 's/ *(([^ ]*) *){[num]}.*//p'
... ou a expressão começará sua correspondência no primeiro caractere do grupo [num]
th (onde [num]
é um inteiro positivo) de [^ ]*
caracteres não-espaciais no padrão -space e continue correspondendo até o final da linha.
O importante, no entanto, é que ele faz subgrupos de algumas correspondências:
-
(([^ ]*) *){[num]}
- este grupo corresponde a tantas ocorrências de [num]
de grupos não espaciais e de qualquer caractere de espaço subseqüente e pode ser referência de volta como
.
-
{[num]}
- quando um padrão é correspondido \{[num]\}
vezes a única referência a ele que permanece é a última - e assim, mesmo que esse grupo corresponda a tantas ocorrências do padrão como especificado, a única referência retornada será a última.
-
([^ ]*)
- o subgrupo do grupo acima, no entanto, corresponde apenas ao subconjunto de caracteres não espaciais correspondidos em
. Este subgrupo pode ser referenciado em
.
-
*
e .*
- corresponde a qualquer / todos os caracteres de espaço que levam ao espaço padrão e a qualquer / todos os caracteres após as ocorrências correspondidas nas subexpressões.
-
//
- isso substitui todos os itens acima apenas pelo grupo referenciado em
.
Como [^ ]*
e *
são complementos booleanos e que [^ ]*
U *
juntos podem descrever qualquer sequência possível, a regex acima funciona universalmente.
Para o seu exemplo:
for n in 1 2 3 4
do echo "abc def ghi" |
sed -E "s/ *(([^ ]*) *){$n}.*//"
done | sed -n l
... imprime ...
abc$
def$
ghi$
$
Como é, ele sempre imprimirá uma linha em branco para uma ocorrência especificada acima da requisitada, mas - se isso não for desejável - a linha pode ser removida da saída inteiramente como:
sed -En 's/ *(([^ ]*) *){[num]}.*//;/./p'
Levando isso um pouco mais longe, a substituição pode ser aplicada globalmente para obter somente cada [num]
th ocorrência. E como *
é bastante limitante, eu farei com [[:space:]]*
- o que corresponderá a qualquer <space><tab><newline><vertical tab><return>
.
s=
{ printf "${s:=$(printf '\r\v\t%10s')}"
seq -s"$s" 100
} | sed -En "s/[${s:=[:space:]}]*(([^$s]*)[$s]*){21}/\
/g; /[^$s]/s/\n*$//p"
Antes de aplicar sed
a ele, o printf ...; seq ...
acima imprime uma única linha como:
\r\v\t 1\r\v\t 2\r\v\t 3\r\v\t...
... e assim por diante. Mas aplicar o sed
acima a ele é:
21
42
63
84
... e nenhum espaço em branco segue os números impressos.