Dlaczego cURL zwraca błąd „(23) Błąd zapisu treści”?

152

Działa dobrze jako pojedyncze narzędzie:

curl "someURL"
curl -o - "someURL"

ale nie działa w potoku:

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

zwraca:

(23) Failed writing body

Jaki jest problem z przesyłaniem potoku na wyjście cURL? Jak zbuforować całe wyjście cURL, a następnie obsłużyć?

statyczny
źródło
1
U mnie to działa, nie ma potrzeby buforowania.
hek2mgl
1
czy to też działa w potoku ?:curl 'http://www.multitran.ru/c/m.exe?CL=1&s=hello&l1=1' | tr -d '\n'
statyczne
1
Dodano tagi osx. Niestety nie mogę w tym pomóc. Używam Linuksa
hek2mgl,
1
problem polegał na kodowaniu strony (cyrilic, win-1251). Muszę więc użyćiconv -f ...
statyczny
5
Tak jak kolejna wskazówka: Mój zawiódł, ponieważ dysk był pełny.
Vince Varga

Odpowiedzi:

113

Dzieje się tak, gdy program potokowy (np. Grep) zamyka potok odczytu przed zakończeniem zapisywania całej strony przez poprzedni program.

W programie curl "url" | grep -qs foo, gdy tylko grep uzyska to, czego chce, zamknie strumień odczytu z curl. cURL nie oczekuje tego i emituje błąd „Błąd zapisu treści”.

Rozwiązaniem jest przepuszczenie strumienia przez program pośredniczący, który zawsze czyta całą stronę przed przekazaniem jej do następnego programu.

Na przykład

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

tacjest prostym programem uniksowym, który czyta całą stronę wejściową i odwraca kolejność wierszy (dlatego uruchamiamy go dwukrotnie). Ponieważ musi odczytać całe wejście, aby znaleźć ostatnią linię, nie wyśle ​​nic do grep, dopóki cURL nie zostanie zakończony. Grep nadal zamyka strumień odczytu, gdy ma to, czego szuka, ale wpłynie tylko na tac, który nie generuje błędu.

Kaworu
źródło
5
Czy nie mógłbyś po prostu przepuścić go catraz? Przynajmniej rozwiązuje problem dla mnie.
benvd
4
Nie. Może to pomóc w przypadku małych dokumentów, ale gdy jest zbyt duży, aby zmieścić się w buforze cat używa, błąd pojawi się ponownie. -sMożesz użyć do wyciszenia wszystkich komunikatów o błędach (i postępu), jeśli ich nie potrzebujesz.
Kaworu
8
tac|taczmienia dane wejściowe, jeśli wejście nie kończy się wysuwem wiersza, lub na przykład printf a\\nb\\nc|tac|tacwypisuje a\ncbgdzie \njest wysuw. Możesz użyć sponge /dev/stdoutzamiast tego. Inną opcją jest printf %s\\n "$(cat)", ale gdy dane wejściowe zawierają bajty o wartości null w powłokach innych niż Zsh, to albo pomija bajty zerowe, albo przestaje czytać po pierwszym bajcie zerowym.
nisetama
Z dokumentacji: CURLE_WRITE_ERROR (23) Wystąpił błąd podczas zapisywania odebranych danych do pliku lokalnego lub błąd został zwrócony do libcurl z wywołania zwrotnego zapisu. curl.haxx.se/libcurl/c/libcurl-errors.html
Jordan Stewart,
2
Ta odpowiedź powinna zostać zaakceptowana, ponieważ wyjaśnia problem, chociaż nie zapewnia odpowiedniego rozwiązania, ponieważ nie ma tacpolecenia w systemie macOS
Dominik Bucher
48

Aby uzyskać kompletność i przyszłe wyszukiwania:

Jest to kwestia tego, jak cURL zarządza buforem, bufor wyłącza strumień wyjściowy opcją -N.

Przykład: curl -s -N "URL" | grep -q Welcome

user5968839
źródło
8
Zadziałało curl -s https://raw.githubusercontent.com/hermitdave/FrequencyWords/master/content/2016/ro/ro_50k.txt | head -20(bez -stego samego błędu).
Dan Dascalescu
24

Inna możliwość, jeśli używasz opcji -o(plik wyjściowy) - katalog docelowy nie istnieje.

na przykład. jeśli masz -o /tmp/download/abc.txti / tmp / download nie istnieje.

Dlatego upewnij się, że wszystkie wymagane katalogi zostały utworzone / istnieją wcześniej, użyj --create-dirsopcji, a także - ojeśli to konieczne

MikeW
źródło
2
Dzięki, --create-dirs rozwiązało to dla mnie w najbardziej niezwykłej sytuacji, nigdy nie mogłem dowiedzieć się, co jest nie tak, ale to był bilet!
rfay
1
Po prostu zdarzyło mi się to w podobnym przypadku. Zapomniałem zadeklarować zmienną $ out na wyjściu. Dzięki, Mike.
Mincong Huang
8

Więc to był problem z kodowaniem. Iconv rozwiązuje problem

curl 'http://www.multitran.ru/c/m.exe?CL=1&s=hello&l1=1' | iconv -f windows-1251 | tr -dc '[:print:]' | ...
statyczny
źródło
8

Możesz to zrobić zamiast używać -oopcji:

curl [url] > [file]


źródło
więc nie używasz potoku i zamiast tego wykonujesz całą pracę nad systemem plików? Chciałem użyć wyjścia curl z rurami.
statyczne
6

Miałem ten sam błąd, ale z innego powodu. W moim przypadku miałem partycję (tmpfs) z tylko 1 GB miejsca i pobierałem duży plik, który w końcu zapełnił całą pamięć na tej partycji i dostałem ten sam błąd co ty.

LLL
źródło
5

W moim przypadku na serwerze zabrakło miejsca na dysku.

Sprawdź to za pomocą df -k .

Zostałem ostrzeżony o braku miejsca na dysku, gdy tacdwukrotnie próbowałem przepuścić , jak opisano w jednej z pozostałych odpowiedzi: https://stackoverflow.com/a/28879552/336694 . Pokazał mi komunikat o błędzie write error: No space left on device.

HostedMetrics.com
źródło
Otrzymałem ten sam błąd z powodu braku miejsca na dysku w kontenerze, ponieważ każdy inny napotkany ten sam problem może wyczyścić miejsce w swoich kontenerach za pomocądocker system prune
Dave,
2

Napotkałem ten komunikat o błędzie podczas próby zainstalowania pamięci podręcznej lakieru na systemie Ubuntu. Wyszukiwarka Google wylądowała tutaj z powodu błędu (23) Failed writing body, dlatego opublikowałem rozwiązanie, które działało dla mnie.

Błąd został napotkany podczas wykonywania polecenia jako root curl -L https://packagecloud.io/varnishcache/varnish5/gpgkey | apt-key add -

rozwiązaniem jest działanie apt-key addbez uprawnień administratora

curl -L https://packagecloud.io/varnishcache/varnish5/gpgkey | apt-key add -
Wszystko jest Vаиітy
źródło
1

Jeśli próbujesz czegoś podobnego source <( curl -sS $url )i otrzymujesz (23) Failed writing bodybłąd, dzieje się tak, ponieważ zastępowanie procesu nie działa bash 3.2(ustawienie domyślne dla macOS).

Zamiast tego możesz skorzystać z tego obejścia.

source /dev/stdin <<<"$( curl -sS $url )"
wisbucky
źródło
0

Dla mnie była to kwestia pozwolenia. Uruchomienie Dockera jest wywoływane z profilem użytkownika, ale root jest użytkownikiem wewnątrz kontenera. Rozwiązaniem było umożliwienie curl zapisu do / tmp, ponieważ ma on uprawnienia do zapisu dla wszystkich użytkowników, a nie tylko dla roota.

Użyłem opcji -o.

-o / tmp / file_to_download

lallolu
źródło
-1

W Bash i zsh (i być może w innych powłokach) możesz użyć podstawiania procesów ( Bash / zsh ), aby utworzyć plik w locie, a następnie użyć go jako danych wejściowych do następnego procesu w łańcuchu potoków.

Na przykład próbowałem przeanalizować dane wyjściowe JSON z cURL przy użyciu jqi less, ale otrzymywałem Failed writing bodybłąd.

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

Kiedy przepisałem go za pomocą zastępowania procesów, zadziałało!

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

Uwaga: jqużywa drugiego argumentu do określenia pliku wejściowego

Bonus: Jeśli używasz jqjak ja i chce zachować pokolorowane wyjście w less, należy użyć następującego wiersza polecenia zamiast:

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

(Dziękuję Kowaru za wyjaśnienie, dlaczego Failed writing body tak się dzieje. Jednak ich rozwiązanie polegające na tacdwukrotnym użyciu nie zadziałało dla mnie. Chciałem również znaleźć rozwiązanie, które będzie lepiej skalować dla dużych plików i próbuję uniknąć innych problemów odnotowanych jako komentarze do tej odpowiedzi.)

Robert
źródło