${string:offset:length}
é um operador de expansão de parâmetro que se expande para o intervalo de caracteres na variável $string
.
Para obter um intervalo de bytes da entrada (e que também se aplica a caracteres de byte único), você pode usar:
func | tail -c +12 | head -c 5
para obter 5 bytes começando com o 12º (offsets baseados em 1). A opção -c
para head
não é padrão, mas é bastante comum.
Observe que func
pode ser eliminado algum tempo depois de ter emitido seu 16º byte, pois head
sairá depois de ter esses 5 bytes, e tail
será eliminado se tentar gravar mais dados depois disso, que iria ondular para func
.
Você também pode fazer:
func | dd bs=1 skip=11 count=5 2> /dev/null
2> /dev/null
é para evitar a mensagem de status no final. Isso suprime todos os erros. Com o GNU dd
, você pode substituí-lo por status=none
para apenas suprimir o status.
Para valores grandes de length
, isso seria menos eficiente ao ler um byte de cada vez. Com o GNU dd
novamente, você pode evitar isso fazendo:
func | dd iflag=count_bytes,skip_bytes,fullblock skip=11 bs=64k count=5M status=none
Que faria tantas leituras de até 64 KiB cada para obter 5 MiB de bytes de dados.
Agora, para aqueles offset e length a serem expressos em caracteres (single ou multi-byte) ao invés de byte, isso se torna mais complicado.
Uma opção é armazenar toda a saída em uma variável e usar o operador ${var:offset:length}
como outras pessoas mostraram . Embora isso signifique armazenar toda a saída na memória. Usar var=$(func)
também significa que os caracteres da nova linha são descartados.
Outra opção é usar bash
de read -N
, que lê um determinado número de caracteres :
func | {
IFS= read -rN 11 discarded
IFS= read -rN 5 data
printf '%s\n' "$data"
}
Ou com perl
(um pouco mais eficiente para dados grandes):
func | perl -Mopen=locale -sne '
BEGIN{$total = $o + $n; $/ = \$total}
print substr($_, $o); exit' -- -o=10000 -n=5000000