【问题标题】:Pipe output through jq ONLY if it is JSON仅当它是 JSON 时才通过 jq 管道输出
【发布时间】:2021-04-08 08:38:30
【问题描述】:

我有以下 BASH 代码:

    response=$( curl -Ls $endpoint )
    if [ -n "$response" ]; then  # nonempty
        echo "$response"  | jq .
    fi

问题是有时响应可能是非空的但不是 JSON(如果不是 200)。

只有当它是有效的 JSON 时,是否可以通过 jq 管道输出?

以下作品:

echo $x | jq . 2>/dev/null  || echo $x

测试:

> x='{"foo":123}'; echo $x | jq . 2>/dev/null || echo "Invalid: $x"
{
  "foo": 123
}

> x='}'; echo $x | jq . 2>/dev/null || echo "Invalid: $x"
Invalid: }

但是,我觉得不舒服。

【问题讨论】:

  • 请注意,echo 并不总是有效,具体取决于实现。一些实现处理反斜杠转义。 printf '%s' "$x" 更可靠。

标签: bash curl conditional-statements jq


【解决方案1】:

如果您想在将响应类型提交给jq 之前对其进行测试,可以从服务器响应中测试Content-Type 标头。

所以您希望curl 使用curl -i 向您发送完整的响应标头和正文。

这是它的一个实现:

#!/usr/bin/env sh

endpoint='https://worldtimeapi.org/api/timezone/Europe/Paris.json'

# Headers and body are delimited by an empty line, with CRLF as the line ending.
# See: RFC7230 HTTP/1.1 Message Syntax and Routing / section 3: Message Format
# https://tools.ietf.org/html/rfc7230#section-3
crlf="$(printf '\r\n_')" # add trailing _ to prevent trailing newline trim
crlf="${crlf%_}" # remove trailing _
http_delim="$crlf$crlf" # RFC7230 section 3

full_http_response="$(curl --silent --include --url "$endpoint")"
http_headers="${full_http_response%$http_delim*}"
http_body="${full_http_response#*$http_delim}"

case $http_headers in
  'HTTP/1.1 200 OK'*'Content-Type: application/json'*)
    # Yes, response body is JSON, so process it with jq.
    jq -n "$http_body"
    ;;
esac

【讨论】:

    【解决方案2】:

    以下作品: 回声 $x | 。 2>/开发/空||回声 $x

    除了这里使用echo之外,这其实是一个很好的方法——它兼具简单和高效的优点。这比直接使用 -e 选项要好,因为 -e 生成的返回码更复杂。

    换句话说,有很多话要说:

    printf "%s" "$x" | jq . 2> /dev/null || printf "%s\n" "$x"
    

    效率

    效率的论据如下:

    • 如果 $x 持有有效的 JSON,则没有开销。

    • 如果 $x 作为 JSON 无效,jq 会很快失败;同样在这种情况下,调用 jq 的开销几乎肯定不会比检查Content-Type 差很多。

    警告

    jq 在没有-e 选项的情况下调用时产生的返回码的官方文档并不完全正确,如下所示:

    $ jq empty <<< 'foo bat' 2> /dev/null ; echo $?
    4
    

    【讨论】:

      【解决方案3】:

      这行得通:

      response=$( curl -Ls -H 'Cache-Control: max-age=0' $endpoint )
      
      if [ -n "$response" ]; then  # nonempty
          echo "Got server response"
      
          # https://stackoverflow.com/questions/46954692/check-if-string-is-a-valid-json-with-jq
          if jq --exit-status type >/dev/null 2>&1 <<<"$response"; then
              # Parsed JSON successfully and got something other than false/null
              echo "$response" | jq .
              echo "... after $i seconds"
              return 0
          else
              echo "Response is not valid JSON"
              echo "$response"
              return 1
          fi
      fi
      

      【讨论】:

      • 这不太对——请参阅 jq 手册中有关指定 -e 选项时的返回码的详细信息。当输入有效时还调用 jq 两次似乎相当浪费。
      猜你喜欢
      • 2011-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-04
      相关资源
      最近更新 更多