Armazenar a saída stderr em uma variável sem modificar o stdout e fazê-lo repetidamente

3

existem muitas perguntas "quase duplicadas", mas nenhuma me deu a resposta que estou procurando, por isso, tenha paciência comigo.

considere uma API de servidor simples, na qual você pode consumir dados em iterações, usando um "token sem estado", que pode ter a seguinte aparência: rQAAMTQ2MzU4MDA1MjgxM3x8fC9wZXJtaWQub3JnfHx8 , portanto:

curl -v "http://ws.foo.bar/_consume?position=rQAAMTQ2MzU4MDA1MjgxM3x8fC9wZXJtaWQub3JnfHx8"
* Connected to ws.foo.bar (12.34.56.78) port 80 (#0)
> GET /_consume?position=rQAAMTQ2MzU4MDA1MjgxM3x8fC9wZXJtaWQub3JnfHx8 HTTP/1.1
> User-Agent: curl/7.35.0
> Host: ws.foo.bar
> Accept: */*
> 
< HTTP/1.1 200 OK
... many irrelevant headers
< X-POSITION: qwAAMTQ2MzU4MDA1MTIxOHx8fC9wZXJtaWQub3JnfHx8
< Connection: close
< Date: Wed, 01 Jun 2016 15:02:42 GMT
{ datadatadatadatadatadatadata... }

gera os dados para stdout e os cabeçalhos (junto com outras informações detalhadas) vão para stderr. um desses cabeçalhos é X-POSITION , armazenando a próxima posição que eu deveria fornecer como parâmetro de consulta para o próximo bloco de dados. Eu tentei criar um one-liner Eu poderia apenas apertar o botão e executar novamente para recuperar o próximo pedaço de dados

Eu tentei o seguinte (assumir a posição inicial: 1foo2BAR3baz4QUUX5blah ):

$ POS=1foo2BAR3baz4QUUX5blah
$ POS=$( curl -v "http://ws.foo.bar/_consume?position=$POS" 2>&1 | grep X-POSITION | awk '{print $3}' )

que obviamente não será impresso para exibir os dados, pois está sendo engolido por grep , mas também POS só muda uma vez e não pode ser usado novamente. onda reclama na segunda invocação:

* Illegal characters found in URL
* Closing connection -1
curl: (3) Illegal characters found in URL

embora pareça que o PDV obteve o novo valor:

$ echo $POS
rQAAMTQ2MzU4MDA1MjgxM3x8fC9wZXJtaWQub3JnfHx8

Eu suspeito, talvez, EOF?

em qualquer caso, mesmo se eu resolver isso, eu ainda não quero ignorar stdout. tentou esta solução :

$ POS='( curl -v "http://ws.foo.bar/_consume?position=$POS" 3>&1 1>&2- 2>&3- ) | grep X-POSITION | awk '{print $3}''

mas parece não funcionar também. não tenho certeza do que fiz de errado, ou se esta é a abordagem correta ...

    
por gilad hoch 01.06.2016 / 17:48

1 resposta

2

O caractere "defeituoso" no parâmetro $POS é EOL. HTTP usa CRLF como final de linha. Isso dá por exemplo:

< X-POSITION: xxxxxxxx\r\n

Isso dá ao campo 3 em awk como xxxxxxxx\r .

Usando print em awk , você também reintroduz a última nova linha, \n , mas como sua expressão não é citada, isso é perdido.

Você pode ver isso fazendo algo como:

curl -v "http://ws.foo.bar/_consume?position=$POS" 2>&1 | cat -v

Aqueles ^M s no final das linhas indicam \r .

Ou:

printf "%s" "$pos" | xxd
00000000: 7251 4141 4d54 5132 4d7a 5534 4d44 4131  rQAAMTQ2MzU4MDA1
00000010: 4d6a 6778 4d33 7838 6643 3977 5a58 4a74  MjgxM3x8fC9wZXJt
00000020: 6157 5175 6233 4a6e 6648 7838 0d         aWQub3JnfHx8.

Esse último 0d é um CR. (Opcionalmente, execute ascii no prompt de comando.)

Também não é necessário misturar grep e awk como awk muito bem por si só.

Para entrar no caminho certo, isso pode ser um começo:

pos=$(curl -v "http://foo.x/pos=$pos" 2>&1 | awk -vRS="\r\n" '/^< X-POSITION:/{printf "%s", $3}')

Aqui, defina o RS , ou separador de registro, em awk para CRLF,

... mas isso só lhe daria o token, não o conteúdo.

Supondo que você não precise realmente imprimir o conteúdo na tela, mas salvá-lo no arquivo de uma maneira poderia ser:

pos=$(curl -sD - -o "$pos.out" "http://foo.x/?position=$pos" | awk -vRS="\r\n" '/^X-POSITION:/{printf "%s", $2}')

Herre nós redirecionamos os dados do cabeçalho para stdout por -D - e salvamos o conteúdo para o arquivo por -o "$pos.out" .

Outra vantagem é que você só analisa os dados do cabeçalho.

    
por 01.06.2016 / 19:41