Por que o cURL retorna o erro "(23) Falha ao escrever o corpo"?

153

Funciona bem como uma única ferramenta:

curl "someURL"
curl -o - "someURL"

mas não funciona em um pipeline:

curl "someURL" | tr -d '\n'
curl -o - "someURL" | tr -d '\n'

retorna:

(23) Failed writing body

Qual é o problema de canalizar a saída cURL? Como armazenar em buffer toda a saída cURL e depois lidar com isso?

estático
fonte
1
Para mim, funciona, não há necessidade de buffer.
Hek2mgl # 23/13
1
isso também funciona no pipeline ?curl 'http://www.multitran.ru/c/m.exe?CL=1&s=hello&l1=1' | tr -d '\n'
estático
1
Adicionadas tags osx. Infelizmente não posso ajudar com isso. Estou usando o Linux
hek2mgl
1
o problema era a codificação da página (cirílico, win-1251). Então eu devo usariconv -f ...
estático
5
Assim como outra dica: o meu falhou, porque o disco estava cheio.
Vince Varga

Respostas:

113

Isso acontece quando um programa canalizado (por exemplo, grep) fecha o canal de leitura antes que o programa anterior termine de escrever a página inteira.

Em curl "url" | grep -qs foo, assim que o grep tiver o que deseja, ele fechará o fluxo de leitura de curl. O cURL não espera isso e emite o erro "Falha na gravação do corpo".

Uma solução alternativa é canalizar o fluxo por meio de um programa intermediário que sempre lê a página inteira antes de enviá-la para o próximo programa.

Por exemplo

curl "url" | tac | tac | grep -qs foo

tacé um programa Unix simples que lê toda a página de entrada e inverte a ordem das linhas (portanto, executamos duas vezes). Como ele precisa ler toda a entrada para encontrar a última linha, ele não produzirá nada para grep até que cURL seja concluído. O Grep ainda fechará o fluxo de leitura quando tiver o que está procurando, mas afetará apenas o tac, que não emite um erro.

Kaworu
fonte
5
Você não poderia simplesmente passar catuma vez? Resolve o problema para mim, pelo menos.
benvd
5
Não. Pode ajudar com documentos pequenos, mas quando for muito grande para caber no buffer, o gato usará o erro reaparecerá. Você pode -ssilenciar todas as mensagens de erro (e avançar) se não precisar delas.
precisa
9
tac|tacaltera a entrada se a entrada não terminar com um avanço de linha ou, por exemplo, printf a\\nb\\nc|tac|tacimprimir a\ncbonde \nestá um avanço de linha. Você pode usar em seu sponge /dev/stdoutlugar. Outra opção é printf %s\\n "$(cat)", mas quando a entrada contém bytes nulos em shells diferentes de Zsh, isso ignora os bytes nulos ou para de ler após o primeiro byte nulo.
Nisetama # 24/16
Dos documentos: CURLE_WRITE_ERROR (23) Ocorreu um erro ao gravar dados recebidos em um arquivo local ou um erro foi retornado à libcurl a partir de um retorno de chamada de gravação. curl.haxx.se/libcurl/c/libcurl-errors.html
Jordan Stewart
3
Esta deve ser a resposta aceita porque explica o problema, altought ele não fornece solução capaz porque não há nenhum taccomando no MacOS
Dominik Bucher
49

Para pesquisas completas e futuras:

É uma questão de como o cURL gerencia o buffer, o buffer desativa o fluxo de saída com a opção -N.

Exemplo: curl -s -N "URL" | grep -q Welcome

user5968839
fonte
8
Funcionou para curl -s https://raw.githubusercontent.com/hermitdave/FrequencyWords/master/content/2016/ro/ro_50k.txt | head -20(sem -seu receber o mesmo erro).
Dan Dascalescu 14/09
24

Outra possibilidade, se usar o -o (arquivo de saída) - o diretório de destino não existe.

por exemplo. se você possui -o /tmp/download/abc.txte / tmp / download não existe.

Portanto, verifique se todos os diretórios necessários foram criados / existentes com antecedência, use a --create-dirsopção e - ose necessário

MikeW
fonte
2
Obrigado, --create-dirs resolveu isso para mim na situação mais incomum, nunca consegui descobrir o que estava errado, mas esse era o bilhete!
Rfay
1
Apenas aconteceu comigo em um caso semelhante. Esqueci de declarar a variável $ out para a saída. Obrigado, Mike.
Mincong Huang 24/09/19
8

Portanto, era um problema de codificação. Iconv resolve o problema

curl 'http://www.multitran.ru/c/m.exe?CL=1&s=hello&l1=1' | iconv -f windows-1251 | tr -dc '[:print:]' | ...
estático
fonte
8

Você pode fazer isso em vez de usar a -oopção:

curl [url] > [file]


fonte
então, não usando o pipe, e sim todo o trabalho no sistema de arquivos? Eu queria usar a saída da onda com tubos.
estática
6

Eu tive o mesmo erro, mas por outro motivo. No meu caso, eu tinha a partição (tmpfs) com apenas 1 GB de espaço e estava baixando um arquivo grande que finalmente preencheu toda a memória dessa partição e obtive o mesmo erro que você.

EU VOU
fonte
5

O servidor ficou sem espaço em disco, no meu caso.

Verifique com df -k .

Fui alertado sobre a falta de espaço em disco quando tentei passar por tacduas vezes, conforme descrito em uma das outras respostas: https://stackoverflow.com/a/28879552/336694 . Ele me mostrou a mensagem de erro write error: No space left on device.

HostedMetrics.com
fonte
Eu recebi o mesmo erro devido à falta de espaço em disco dentro de um recipiente, por qualquer outra pessoa também bater o mesmo problema pode limpar o espaço dentro de seus recipientes comdocker system prune
Dave
2

Encontrei essa mensagem de erro ao tentar instalar o cache de verniz no ubuntu. A pesquisa no google me levou aqui para o erro (23) Failed writing body, postando uma solução que funcionou para mim.

O erro é encontrado ao executar o comando como root curl -L https://packagecloud.io/varnishcache/varnish5/gpgkey | apt-key add -

a solução é executar apt-key addcomo não raiz

curl -L https://packagecloud.io/varnishcache/varnish5/gpgkey | apt-key add -
Tudo é muito
fonte
1

Se você está tentando algo semelhante source <( curl -sS $url )e obtendo o (23) Failed writing bodyerro, é porque a aquisição de uma substituição de processo não funciona embash 3.2 (o padrão para o macOS).

Em vez disso, você pode usar esta solução alternativa.

source /dev/stdin <<<"$( curl -sS $url )"
wisbucky
fonte
0

Para mim, era questão de permissão. A execução do Docker é chamada com um perfil de usuário, mas root é o usuário dentro do contêiner. A solução foi fazer a gravação curl em / tmp, pois ela tem permissão de gravação para todos os usuários, não apenas para raiz.

Eu usei a opção -o.

-o / tmp / file_to_download

lallolu
fonte
-1

No Bash e no zsh (e talvez em outros shells), você pode usar a substituição de processo ( Bash / zsh ) para criar um arquivo rapidamente e, em seguida, usá-lo como entrada para o próximo processo na cadeia de pipeline.

Por exemplo, eu estava tentando analisar a saída JSON do cURL usando jqe less, mas estava obtendo o Failed writing bodyerro.

# Note: this does NOT work
curl https://gitlab.com/api/v4/projects/ | jq | less

Quando eu o reescrevi usando substituição de processo, funcionou!

# this works!
jq "" <(curl https://gitlab.com/api/v4/projects/) | less

Nota: jqusa seu segundo argumento para especificar um arquivo de entrada

Bônus: Se você estiver usando jqcomo eu e quer manter a saída colorizado em less, use a seguinte linha de comando em vez disso:

jq -C "" <(curl https://gitlab.com/api/v4/projects/) | less -r

(Obrigado a Kowaru pela explicação sobre o motivo da Failed writing body ocorrência. No entanto, a solução de usar tacduas vezes não funcionou para mim. Eu também queria encontrar uma solução que fosse melhor dimensionada para arquivos grandes e tente evitar os outros problemas mencionados como comentários. para essa resposta.)

Robert
fonte