通过 curl 获取响应主体并显示 HTTP 代码

通过 curl 获取响应主体并显示 HTTP 代码

我有一个返回 JSON(响应主体)的端点。我需要通过 curl 获取响应主体并对其进行处理(例如使用 jq)。它的工作原理如下:

response=$(curl -s https://swapi.dev/api/people/1?format=json)
name=$(echo $response tmpFile | jq '.name') # irrelevant command, but I need here response body
echo "name:"$name

但我还需要显示 HTTP 代码(以显示请求是否成功):

curl -s -w "%{http_code}\n" -o /dev/null https://swapi.dev/api/people/1?format=json

如何将响应主体传递给变量并同时显示 HTTP 代码(一个请求)?


我找到了临时文件的解决方案:

touch tmpFile
curl -s -w "%{http_code}\n" -o tmpFile https://swapi.dev/api/people/1?format=json
name=$(cat tmpFile | jq '.name') # irrelevant command, but I need here only body response
echo "name: "$name
rm tmpFile

不创建文件怎么办?

我尝试使用命名管道(但它仍然需要在磁盘上创建文件...):

mkfifo tmpFifo
curl -s -w "%{http_code}\n" -o tmpFifo https://swapi.dev/api/people/1?format=json
name=$(cat tmpFifo | jq '.name') # irrelevant command, but I need here only body response
echo "name: "$name
rm tmpFifo

但命名管道未被删除。

有没有无需创建任何文件的解决方案,例如仅创建变量或流?

答案1

看起来响应的内容只有一行。您可以使用两次read调用来读取两行:

curl -s -w "\n%{http_code}" 'https://swapi.dev/api/people/1?format=json' | {
    read body
    read code
    echo $code
    jq .name <<< "$body"
}

答案2

最后一行有返回主体和 HTTP 代码的解决方案:

response=$(curl -s -w "\n%{http_code}" https://swapi.co/api/people/1/?format=json)
response=(${response[@]}) # convert to array
code=${response[-1]} # get last element (last line)
body=${response[@]::${#response[@]}-1} # get all elements except last
name=$(echo $body | jq '.name')
echo $code
echo "name: "$name

但是我仍然宁愿使用两个单独的变量/流来执行此操作,而不是在一个变量中连接响应主体和 HTTP 代码。

答案3

这是一个简单的 bash 双行命令,它将返回状态代码和内容,并处理单行或多行文档正文。

如果我们运行下面的脚本,curl 会将文档正文和 HTTP 状态代码写入标准输出。

URL="https://superuser.com/questions/1320674/get-response-body-and-show-http-code-by-curl" 
curl -s -w "%{http_code}\n" $URL

如果我们重新排序这些,以便首先输出 HTTP 状态代码,然后输出文档正文,我们可以利用 bash 内置的功能read将所有内容塞进一个 shell 变量中。

man builtins

...
read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]
      One line is read from the standard input, or from the file descriptor fd supplied as an argument to the -u option, split into words as  described  in  bash(1)  under  Word
      Splitting,  and  the  first word is assigned to the first name, the second word to the second name, and so on.  If there are more words than names, the remaining words and
      their intervening delimiters are assigned to the last name.  If there are fewer words read from the input stream than names, the remaining names are assigned empty values.
      The  characters  in IFS are used to split the line into words using the same rules the shell uses for expansion (described in bash(1) under Word Splitting).  The backslash
      character (\) may be used to remove any special meaning for the next character read and for line continuation.  Options, if supplied, have the following meanings:
...

通过将输出写入文件可以重新排序 curl 输出。 curl 将-w, --write-out首先将选项请求的信息发送到 stdout。 写入主体的文件将替换为使用进程替换 >(),并将正文发送到此子进程的标准输入。在此子进程中,该read命令用于通过使用-u0选项指定输入流来从标准输入中获取文档正文。IFS=禁用分词,这样空格将保持不变。正文保存到名为的变量中stdin,该变量的命名纯粹是为了便于引用。printf用于将此变量的值写入标准输出。

IFS=$'\n' read -r -d '' http_status body < <(curl -s -w "%{http_code}\n" -o \
  >(IFS= read -r -d '' -u0 stdin; printf "%s"  "$stdin") $URL)

现在数据在 stdout 中已正确排序,我们使用后续进程替换将其发送到read父 shell 进程中的另一个命令,该命令会将其放入 shell 变量中。IFS=$'\n'将单词拆分设置为仅在新行上发生。第一个单词是 HTTP 状态代码,因此将其放入变量中http_status。所有后续单词都放入变量中body-r选项 toread用于禁用文档正文数据的反斜杠转义处理。该-d ''选项将分隔符设置为空以确保read使用所有内容。

echo $http_status
200
echo $body | head -8                                                                                                                                          
<!DOCTYPE html>


    <html itemscope itemtype="https://schema.org/QAPage" class="html__responsive " lang="en">

    <head>

        <title>bash - Get response body and show HTTP code by curl - Super User</title>

答案4

尽管我们将 http_code 作为换行符附加,http_code 仍显示为最后一行。我认为这是因为手册页中描述了:“-w在传输完成后,使 curl 在 stdout 上显示信息”。

解决方案是使用 tail + sed 通过文本操作获取正确的变量:

URL="https://swapi.dev/api/people/1?format=json";
response="$(curl -s -w "\n%{http_code}" $URL)";

http_body="$(echo "$response" | sed '$d' | jq .name)";
http_status_code="$(echo "$response" | tail -n 1)";

echo "$http_body";
echo "$http_status_code";

命令细分:
sed '$d'- 删除最后一行并返回其余部分,这对于获取 html 正文很有用。
tail -n 1- 提供除最后一行以外的所有内容,这对于获取状态代码很有用。

相关内容