Почему cURL возвращает ошибку «(23) Ошибка записи тела»?


166

Он работает как единый инструмент:

curl "someURL"
curl -o - "someURL"

но он не работает в конвейере:

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

он возвращает:

(23) Failed writing body

В чем проблема с конвейером вывода cURL? Как буферизовать весь вывод cURL, а затем обработать его?


1
Для меня это работает, буферизация не требуется.
hek2mgl

1
это тоже работает в конвейере ?:curl 'http://www.multitran.ru/c/m.exe?CL=1&s=hello&l1=1' | tr -d '\n'
static

1
Добавлены теги osx. К сожалению, я не могу с этим помочь. Я использую Linux
hek2mgl

1
проблема заключалась в кодировке страницы (кириллица, win-1251). Поэтому я должен использоватьiconv -f ...
static

5
Просто еще один намек: мой не удался, потому что диск был заполнен.
Винс Варга

Ответы:


120

Это происходит, когда программа с конвейером (например, grep) закрывает конвейер чтения до того, как предыдущая программа завершила запись всей страницы.

In curl "url" | grep -qs foo, как только grep получит то, что хочет, он закроет поток чтения из curl. cURL этого не ожидает и выдает ошибку «Ошибка записи тела».

Обходной путь - направить поток через промежуточную программу, которая всегда считывает всю страницу, прежде чем передать ее следующей программе.

Например

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

tac- это простая программа Unix, которая считывает всю страницу ввода и меняет порядок строк (поэтому мы запускаем ее дважды). Поскольку он должен прочитать весь ввод, чтобы найти последнюю строку, он не будет ничего выводить в grep до завершения cURL. Grep все равно закроет поток чтения, когда получит то, что ищет, но повлияет только на tac, который не выдаст ошибку.


5
Не могли бы вы просто пропустить его catодин раз? По крайней мере, решает проблему для меня.
Benvd

5
Нет. Это может помочь с небольшими документами, но если они слишком велики, чтобы поместиться в буфер, который использует кошка, ошибка появится снова. Вы можете использовать, -sчтобы заглушить все сообщения об ошибках (и прогресс), если они вам не нужны.
Kaworu

9
tac|tacизменяет ввод, если ввод не заканчивается переводом строки, или, например, printf a\\nb\\nc|tac|tacпечатает, a\ncbгде \nэто перевод строки. sponge /dev/stdoutВместо этого можно использовать . Другой вариант printf %s\\n "$(cat)", но когда вход содержит нулевые байты в оболочках, отличных от Zsh, он либо пропускает нулевые байты, либо прекращает чтение после первого нулевого байта.
nisetama

Из документации: CURLE_WRITE_ERROR (23) Произошла ошибка при записи полученных данных в локальный файл, или ошибка была возвращена libcurl из обратного вызова записи. curl.haxx.se/libcurl/c/libcurl-errors.html
Джордан Стюарт,

3
Это должен быть принятый ответ, потому что он объясняет проблему, хотя он не предлагает подходящего решения, поскольку tacв macOS нет команды
Доминик Бухер

52

Для полноты и будущих поисков:

Это вопрос того, как cURL управляет буфером, буфер отключает выходной поток с параметром -N.

Пример: curl -s -N "URL" | grep -q Welcome


8
Это сработало curl -s https://raw.githubusercontent.com/hermitdave/FrequencyWords/master/content/2016/ro/ro_50k.txt | head -20(без -sтой же ошибки).
Дэн Даскалеску

30

Другая возможность, если использовать -o опция (выходной файл) - целевой каталог не существует.

например. если у вас есть-o /tmp/download/abc.txt и / tmp / download не существует.

Следовательно, убедитесь, что все необходимые каталоги созданы / существуют заранее, используйте --create-dirsпараметр, а также - oпри необходимости


2
Спасибо, --create-dirs решил это для меня в самой необычной ситуации, никогда не мог понять, что не так, но это был билет!
rfay

1
Так случилось со мной в подобном случае. Я забыл объявить переменную $ out для вывода. Спасибо, Майк.
Mincong Huang,

9

Вы можете сделать это вместо использования -oопции:

curl [url] > [file]


Итак, не использовать канал, а вместо этого выполнять всю работу над файловой системой? Я хотел использовать вывод curl с трубами.
статические,

8

Так что это была проблема с кодировкой. Iconv решает проблему

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

6

У меня была такая же ошибка, но по другой причине. В моем случае у меня был раздел (tmpfs) с объемом всего 1 ГБ, и я загружал большой файл, который, наконец, заполнил всю память на этом разделе, и я получил ту же ошибку, что и вы.


6

В моем случае на сервере закончилось место на диске.

Проверьте это с помощью df -k .

Я был предупрежден о нехватке места на диске, когда tacдважды пытался выполнить подключение по конвейеру , как описано в одном из других ответов: https://stackoverflow.com/a/28879552/336694 . Он показал мне сообщение об ошибке write error: No space left on device.


Я получил ту же ошибку из-за нехватки места на диске в контейнере, потому что любой, кто сталкивается с той же проблемой, может очистить место в своих контейнерах с помощьюdocker system prune
Дэйв

2

Я столкнулся с этим сообщением об ошибке при попытке установить кеш-память varnish на ubuntu. Поиск в Google привел меня сюда из-за ошибки (23) Failed writing body, поэтому я опубликовал решение, которое сработало для меня.

Ошибка возникает при запуске команды от имени пользователя root curl -L https://packagecloud.io/varnishcache/varnish5/gpgkey | apt-key add -

решение - запустить apt-key addкак не root

curl -L https://packagecloud.io/varnishcache/varnish5/gpgkey | apt-key add -

2

Для меня это была проблема с разрешением. Запуск Docker вызывается с профилем пользователя, но root - это пользователь внутри контейнера. Решением было заставить curl записывать в / tmp, поскольку у него есть права на запись для всех пользователей, а не только для root.

Я использовал параметр -o.

-o / tmp / file_to_download


1

Если вы пытаетесь сделать что-то подобное, например source <( curl -sS $url )и получаете сообщение об (23) Failed writing bodyошибке, это потому, что поиск замены процесса не работает bash 3.2(по умолчанию для macOS).

Вместо этого вы можете использовать этот обходной путь.

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

-2

В Bash и zsh (и, возможно, в других оболочках) вы можете использовать замену процесса ( Bash / zsh ) для создания файла на лету, а затем использовать его в качестве входных данных для следующего процесса в цепочке конвейера.

Например, я пытался разобрать вывод JSON из cURL с помощью jqи less, но получал Failed writing bodyошибку.

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

Когда я его переписал с подстановкой процесса, все заработало!

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

Примечание: jqиспользует второй аргумент для указания входного файла

Бонус: Если вы используете , jqкак я и хотите , чтобы держать Раскрашенную выход в less, используйте следующую командную строку вместо того, чтобы :

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

(Спасибо Kowaru за их объяснение, почему Failed writing body это произошло. Однако их решение использовать tacдважды не сработало для меня. Я также хотел найти решение, которое лучше масштабировалось бы для больших файлов, и пытается избежать других проблем, отмеченных как комментарии на этот ответ.)

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.