Almacenar la salida de stderr en una variable sin modificar stdout, y hacerlo de manera repetida

hay muchas preguntas "casi duplicadas", pero ninguna me dio la respuesta que estoy buscando, así que tengan paciencia conmigo.

considere una API de server simple, en la que puede consumir datos en iteraciones, utilizando un "token sin estado", que podría ser similar a: rQAAMTQ2MzU4MDA1MjgxM3x8fC9wZXJtaWQub3JnfHx8 , por lo que:

 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... } 

envía los datos a stdout y los encabezados (junto con otra información detallada) a stderr. uno de estos encabezados es X-POSITION , almacenando la siguiente position que debería proporcionar como parámetro de consulta para el siguiente fragment de datos. Traté de encontrar un juego de una sola línea. Podría presionar el button y ejecutar nuevamente para recuperar el siguiente fragment de datos.

Intenté lo siguiente (asumir la position inicial: 1foo2BAR3baz4QUUX5blah ):

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

que obviamente no se imprimirá para proteger los datos, ya que grep lo va a tragar, pero también, el POS solo cambia una vez y no se puede volver a usar. curl se queja en la segunda invocación:

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

aunque parece que POS obtuvo el nuevo valor:

 $ echo $POS rQAAMTQ2MzU4MDA1MjgxM3x8fC9wZXJtaWQub3JnfHx8 

Sospecho que tal vez EOF?

en cualquier caso, incluso si resuelvo esto, todavía quiero no ignorar stdout. intentado esta solución :

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

pero parece que tampoco funciona. no estoy seguro de lo que hice mal, o si este es el enfoque correcto …

El carácter "defectuoso" en el parámetro $POS es EOL. HTTP usa CRLF como fin de línea. Eso da, por ejemplo:

 < X-POSITION: xxxxxxxx\r\n 

Eso le da al campo 3 en awk ser xxxxxxxx\r .

Al usar print in awk , también vuelve a introducir la última línea nueva, \n , pero como su expresión no se cita, se pierde.

Puedes ver esto haciendo algo como:

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

Esas ^M s al final de las líneas indican \r .

O:

 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. 

Ese último 0d es un CR. (Opcionalmente, ascii en el símbolo del sistema).

Tampoco es necesario mezclar grep y awk como awk mathces bien por sí mismo.

Para seguir el path correcto, esto podría ser un comienzo:

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

Aquí uno establece el RS , o separador de loggings, en awk a CRLF,

… pero esto solo te daría la ficha, no el contenido.

Suponiendo que no necesita imprimir realmente el contenido en la pantalla, pero savelo en un file de una manera podría ser:

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

Herre, networkingirigimos los datos de encabezado a stdout por -D - y -o "$pos.out" contenido en file por -o "$pos.out" .

Otra ventaja de esto es que solo analiza los datos del encabezado.