$ cat file | curl -F 'sprunge=<-' http://sprunge.us

So the output of echo gets passed as a POST parameter to cURL. Is this a cURL specific feature?

  • You can use curl -F 'sprunge=<file' http://sprunge.us instead. – user26112 Jul 22 '13 at 4:53

- is commonly used to represent standard input and < is commonly used to represent redirection from a file. I believe those syntaxes come from early shells. Together, they imply taking in standard input and sending/redirecting it elsewhere. The syntax is almost natural.

Looking at the cURL revision history, the < syntax was added to cURL in mid-2000. The revision that added this feature is available as Git commit 5b7a5046e6.

From the changelog,

Torsten Foertsch <torsten.foertsch at gmx.net> brought a set of fixes for
the rfc1867 form posts. He introduced 'name=<file' which brings a means to
suuply very large text chunks read from the given file name. It differs from
'name=@file' in the way that this latter thing is marked in the uploaded
contents as a file upload, while the first is just text (as in a input or
textarea field). Torsten also corrected a bug that would happen if you used
%s or similar in a -F file name.

There is no mention of the inspiration or origin of this feature.

The @- syntax was present in cURL in the earliest version of the source I could find. From the first revision in late 1999,

/* postfield data */
if('@' == *nextarg) {
  /* the data begins with a '@' letter, it means that a file name
     or - (stdin) follows */
  FILE *file;
  nextarg++; /* pass the @ */

It's difficult to determine if it is cURL-specific. The syntax is common and natural. The cURL feature with which it is associated is a base feature of cURL. Tools similar to cURL are likely to implement some form if it.

The original question asked about

$ echo foo | curl -d 'sprunge=<-' http://sprunge.us

Here was my answer:

I do not believe that is a feature of cURL.

$ # Terminal A
$ curl --version
curl 7.31.0 (x86_64-unknown-linux-gnu) libcurl/7.31.0 OpenSSL/1.0.1e zlib/1.2.8 libssh2/1.4.3
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp scp sftp smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP
$ echo foo | curl -d 'sprunge=<-' localhost:2222

$ # Terminal B
$ nc -l 2222
User-Agent: curl/7.31.0
Host: localhost:2222
Accept: */*
Content-Length: 7
Content-Type: application/x-www-form-urlencoded


I couldn't find any mention of this feature in the cURL documentation. There is a similar feature though.

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. The contents of the file must already be URL-encoded. Multiple files can also be specified. Posting data from a file named 'foobar' would thus be done with --data @foobar.

  • Sorry, I didn't copy the exact output. I ran it with the word test but tried to modify it to reflect the question. I've fixed my answer. – user26112 Jul 22 '13 at 4:31
  • Yeah I could tell you were refining the answer, just pointing that stuff out. I liked your idea in using nc to test this. I always forget about little nc. – slm Jul 22 '13 at 4:33
  • It's in my man page. Look for -d. I hope you don't mind, I didn't understand what the "sprunge=<-" was until I saw your answer and realized it was data being sent in the POST. – slm Jul 22 '13 at 4:36
  • @slm: Thanks for pointing things out. I appreciate it. – user26112 Jul 22 '13 at 4:38
  • Sorry, wrong command. – Jürgen Paul Jul 22 '13 at 4:40

Spying on curl with socat

The updated question regarding this command:

$ cat file | curl -F 'sprunge=<-' http://sprunge.us

Is doing several things. Using socat we can spy on the request like so in one terminal:

$ socat - TCP4-LISTEN:2222,fork | grep -E 'Content-Disp|msg'

Now in a second terminal we'll use your curl command to connect to our socat daemon. For the cat file we're going to use this as our sample file:

$ cat hello.txt
msg: hello curl

And when we curl:

$ cat ~/hello.txt | curl -Fblah=\<- localhost:2222

We see this in the socat output:

Content-Disposition: form-data; name="blah"
msg: hello curl

If we change the string from blah to a - we'll see the following:

$ cat ~/hello.txt | curl -F-=\<- localhost:2222


Content-Disposition: form-data; name="-"

So as we can see, the argument after the initial -F is the name of the form we want to submit against. The man page for curl mentions that-F` is for submitting a HTTP form where we want to specify the name:

 -F, --form <name=content>
   (HTTP)  This  lets  curl emulate a filled-in form in which a user
   has pressed the submit button. This causes curl to POST data using
   the Content-Type  multipart/form-data  according  to  RFC  2388.
   This enables uploading of binary files etc. To force the 'content'
   part to be a file,  prefix  the  file  name with  an  @  sign.
   To just get the content part from a file, prefix the file
   name with the symbol <. The difference between @ and < is then
   that @ makes a file  get  attached  in  the  post as a file upload,
   while the < makes a text field and just get the contents for that
   text field from a file.

The rest of the switches to the -F-= switch are connecting the STDIN input to this argument. <-. STDIN will contain a stream of the content coming in via the cat file |.

Comparing args - '-F-=<-' vs. -F-=\<-

These 2 notations are identical. Again we can use additional verbosity to see what's happening.

$ set -x; cat ~/hello.txt | curl '-F-=<-' localhost:2222; set +x
+ cat /Users/smingolelli/hello.txt
+ curl '-F-=<-' localhost:2222

Whereas the other method:

$ set -x; cat ~/hello.txt | curl -F-=\<- localhost:2222; set +x
+ cat /Users/smingolelli/hello.txt
+ curl '-F-=<-' localhost:2222

People like to use the first method because it saves them an extra character in typing it. But from curl's perspective, they're identical. All that -F-=\<- is doing is escaping the redirect so that curl gets to see it instead of the shell processing it.

Original Quesiton

The original question asked about this:

$ echo foo | curl -d 'sprunge=<-' http://sprunge.us

To which I answered:

When you use the switch -d to curl you're implying a POST, from the curl man page.

-d/--data <data>
     (HTTP) Sends the specified data in a POST request to the HTTP server, 
     in the same way that a browser does  when  a user has filled in an 
     HTML form and presses the submit button. This will cause curl to pass 
     the data to the server using the content-type 
     application/x-www-form-urlencoded.  Compare to -F/--form.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.