115

I'm trying to pass the cat output to curl:

$ cat file | curl --data '{"title":"mytitle","input":"-"}' http://api

But input is literally a -.

Peter Turner
  • 11,199
  • 10
  • 68
  • 109
Jürgen Paul
  • 14,299
  • 26
  • 93
  • 133
  • I'm not sure if there is a special format here, otherwise this might be what you want: http://serverfault.com/questions/313599/how-do-i-pipe-the-output-of-uptime-df-to-curl – thomasa88 May 28 '13 at 15:28

8 Answers8

225

I spent a while trying to figure this out and got it working with the following:

cat data.json | curl -H "Content-Type: application/json" -X POST --data-binary @- http://api
Chris Stryczynski
  • 30,145
  • 48
  • 175
  • 286
t j
  • 7,026
  • 12
  • 46
  • 66
  • 1
    the problem is the content of the file will be the value of `input` in the JSON data. – Jürgen Paul Aug 22 '14 at 08:28
  • 2
    I keep forgetting the `@` symbol. :( – Christopher Schultz Jun 25 '20 at 21:09
  • 7
    You should be aware that `-d` skips `\n` and `\r` characters. That's why I always use `--data-binary` – Nikolay Dimitrov Jul 21 '20 at 10:15
  • Looks like this also works if you use `< data.json` or a heredoc (e.g. `<< EOM`) as opposed to piping with `cat`, if you want. e.g. `curl -H 'content-type: application/json' -XPOST --data-binary @- < data.json`. Looks like `--data-raw` might also work in place of `--data-binary`, but not sure if it strips newlines. – redbmk Aug 13 '22 at 01:34
  • oh, or just reading the other answers apparently just `@data.json` should work instead of piping from cat. Seems pretty flexible – redbmk Aug 13 '22 at 01:36
29

You can use the magical stdin file /dev/stdin

cat data.json | curl -H "Content-Type: application/json" -X POST -d "$(</dev/stdin)" http://api
Nathan
  • 431
  • 5
  • 8
  • 6
    And what if `/dev/stdin` is 25MB, like in my use case where I need to upload a 25MB file and 25MB of base64 text is too much for bash-arguments to handle? – Braden Best Mar 30 '18 at 07:15
  • 3
    Beware of string substitution, especially for binary data. This may subtly corrupt your data (e.g. strip a trailing newline or drop everything after a NUL). – Vladimir Panteleev Sep 14 '19 at 21:23
  • This works perfect in bash, but yields `error when reading /dev/stdin: Input/output error` in zsh – Carson May 12 '20 at 22:33
  • https://stackoverflow.com/a/57285781/57357 works for zsh – Dan Tanner Aug 02 '21 at 18:20
17

If you want to type/paste the data without escaping or polluting your bash history then, you can use this

cat | curl -H 'Content-Type: application/json' http://api -d @-

Which drops you into cat where you can input the data, directly, e.g. Shift + Insert in your terminal. You finish with a newline and a Ctrl + D which signals to cat that you're done. That data is then passed to curl, and you have a reusable history entry.

Walf
  • 8,535
  • 2
  • 44
  • 59
16

This should also work

curl -H "Content-Type: application/json" -d @data.json http://api

Using -d forces curl to implicitly use POST for the request.

Joseph King
  • 5,089
  • 1
  • 30
  • 37
15
# Create the input file
echo -n 'Try  and " to verify proper JSON encoding.' > file.txt

# 1. Use jq to read the file into variable named `input` 
# 2. create the desired json
# 3. pipe the result into curl
jq -n --rawfile input file.txt '{"title":"mytitle", $input}' \
| curl -v 'https://httpbin.org/post' -H 'Content-Type: application/json' -d@- 

Output:

  ...
  "json": {
    "input": "Try \ud83d\ude01 and \" to verify proper JSON encoding.", 
    "title": "mytitle"
  }, 
  ...

Notice that the contents of the input file was properly escaped for using as a JSON value.

jq options used:

  • --null-input/-n:
    Don’t read any input
  • --rawfile variable-name filename:
    This option reads in the named file and binds its contents to the given global variable.

See jq manual for full details.

The -d@- option tells curl to read the data from STDIN.

Robin A. Meade
  • 1,946
  • 18
  • 17
7

Curl documentation for -d option

If you start the data with the letter @, the rest should be a file name to read the data from, or - if you want curl to read the data from stdin. Multiple files can also be specified. Posting data from a file named 'foobar' would thus be done with -d, --data @foobar. When --data is told to read from a file like that, carriage returns and newlines will be stripped out. If you don't want the @ character to have a special interpretation use --data-raw instead.

Depending of your HTTP endpoint, server configuration, you should be good by using this format:

curl -d @data.json http://api

richerlariviere
  • 799
  • 2
  • 13
  • 29
6

Try

curl --data '{"title":"mytitle","input":"'$(cat file)'-"}' http://api
golimar
  • 2,419
  • 1
  • 22
  • 33
3

Sounds like you want to wrap the content of input in a JSON body, and then have that sent over with a POST request. I think that the simplest way to do that is to manipulate stdin first and then push that over to curl using -d @-. One way could look like this:

cat <(echo '{"title":"mytitle","input":"') file <(echo '"}') \
| curl -d @- http://api

I'm using <(echo) to use cat to merge strings and files, but there is almost certainly a better way.

Keep in mind that this does not escape the contents of file and that you may run into issues because of that.

zneak
  • 134,922
  • 42
  • 253
  • 328