Como fazer o eco de uma substring da saída de um pipe?

2

Eu encontrei aqui como extrair uma substring no bash, mas não sei como aplicar isso depois um cano. Por exemplo:

some func | echo ${string:12:5}

Como atribuo a saída de some func à variável string ?

    
por Michael Hays 11.05.2018 / 17:38

3 respostas

4

${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
    
por 11.05.2018 / 18:30
2
string="$(func)"
echo "${string:12:5}"
    
por 11.05.2018 / 17:44
2

Resposta

Se você deseja apenas extrair a saída de some_func , não precisa armazená-la em uma variável, basta enviar a saída para cut , o que extrairá os caracteres solicitados:

some_func | cut -c 12-16  

Explicação

cut Receberá o stdin e extrairá o intervalo solicitado com base nas opções especificadas.

-c Significa que o intervalo é especificado em caracteres.

12-16 Intervalo em que os caracteres começam com o índice 1 , não 0 .
Portanto, isso levará os caracteres na posição 12 , 13 , 14 , 15 e 16 .

Como Stéphane Chazelas me lembrou, observe que isso funcionará para cada linha de entrada, não apenas a primeira linha.

    
por 11.05.2018 / 17:45