![将 stderr 输出存储在变量中而不修改 stdout,并重复执行此操作](https://linux22.com/image/8063/%E5%B0%86%20stderr%20%E8%BE%93%E5%87%BA%E5%AD%98%E5%82%A8%E5%9C%A8%E5%8F%98%E9%87%8F%E4%B8%AD%E8%80%8C%E4%B8%8D%E4%BF%AE%E6%94%B9%20stdout%EF%BC%8C%E5%B9%B6%E9%87%8D%E5%A4%8D%E6%89%A7%E8%A1%8C%E6%AD%A4%E6%93%8D%E4%BD%9C.png)
考虑一个简单的服务器 API,您可以在其中使用“无状态令牌”迭代使用数据,它可能如下所示:rQAAMTQ2MzU4MDA1MjgxM3x8fC9wZXJtaWQub3JnfHx8
,因此:
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... }
将数据输出到 stdout,标头(以及其他详细信息)转到 stderr。其中一个标头是X-POSITION
,存储我应该提供的下一个位置作为下一个数据块的查询参数。我试图想出一个简单的方法,我只需点击按钮并再次执行即可检索下一个数据块
我尝试了以下操作(假设初始位置1foo2BAR3baz4QUUX5blah
:):
$ POS=1foo2BAR3baz4QUUX5blah
$ POS=$( curl -v "http://ws.foo.bar/_consume?position=$POS" 2>&1 | grep X-POSITION | awk '{print $3}' )
显然,它不会打印到屏幕数据,因为它被 吞没了grep
,而且,POS
只改变一次,并且不能再次使用。第二次调用时,curl 抱怨:
* Illegal characters found in URL
* Closing connection -1
curl: (3) Illegal characters found in URL
尽管 POS 似乎获得了新的价值:
$ echo $POS
rQAAMTQ2MzU4MDA1MjgxM3x8fC9wZXJtaWQub3JnfHx8
我怀疑也许是EOF?
无论如何,即使我解决了这个问题,我仍然不想忽略标准输出。尝试过这个解决方案:
$ POS=`( curl -v "http://ws.foo.bar/_consume?position=$POS" 3>&1 1>&2- 2>&3- ) | grep X-POSITION | awk '{print $3}'`
但它似乎也不起作用。
答案1
这“有缺陷”参数中的字符$POS
为 EOL。 HTTP 使用 CRLF 作为行结束符。例如:
< X-POSITION: xxxxxxxx\r\n
这使得 中的字段 3awk
为xxxxxxxx\r
。
通过使用print
inawk
您还可以重新引入最后一个换行符 ,\n
但是由于您的表达式没有被引用,因此它会丢失。
您可以通过执行以下操作来看到这一点:
curl -v "http://ws.foo.bar/_consume?position=$POS" 2>&1 | cat -v
^M
行尾的 s表示\r
。
或者:
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.
最后一个0d
是 CR。 (可以选择ascii
在命令提示符下执行。)
也不需要混合grep
,awk
因为awk
数学本身就很好。
为了走上正确的轨道,这可能是一个开始:
pos=$(curl -v "http://foo.x/pos=$pos" 2>&1 | awk -vRS="\r\n" '/^< X-POSITION:/{printf "%s", $3}')
这里将RS
或记录分隔符设置awk
为 CRLF,
...但这只会给你令牌,而不是内容。
假设您不需要实际将内容打印到屏幕上,但将其保存到文件中的一种方法可能是:
pos=$(curl -sD - -o "$pos.out" "http://foo.x/?position=$pos" | awk -vRS="\r\n" '/^X-POSITION:/{printf "%s", $2}')
在这里,我们将标头数据重定向到stdout
by-D -
并将内容保存到文件 by -o "$pos.out"
。
这样做的另一个优点是您只需解析标头数据。