curl: add long option '--out-null'

Add a new commandline option --out-null that discards all
response bytes into the void. Replaces non-portable use of
'-o /dev/null' with more efficiency.

Feature added in 8.16.0

Closes #17800
This commit is contained in:
Stefan Eissing 2025-07-02 09:43:13 +02:00 committed by Daniel Stenberg
parent 0e49234128
commit 6845533e24
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
13 changed files with 137 additions and 6 deletions

View File

@ -181,6 +181,7 @@ DPAGES = \
ntlm.md \
oauth2-bearer.md \
output-dir.md \
out-null.md \
output.md \
parallel-immediate.md \
parallel-max.md \

View File

@ -0,0 +1,26 @@
---
c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
SPDX-License-Identifier: curl
Long: out-null
Help: Discard response data into the void
Category: output
Added: 8.16.0
Multi: per-URL
See-also:
- output
- remote-name
- remote-name-all
- remote-header-name
Example:
- "https://example.com" --out-null
---
# `--out-null`
Discard all response output of a transfer silently. This is the more
efficient and portable version of
curl https://host.example -o /dev/null
The transfer is done in full, all data is received and checked, but
the bytes are not written anywhere.

View File

@ -9,6 +9,7 @@ Category: important output
Added: 4.0
Multi: per-URL
See-also:
- out-null
- remote-name
- remote-name-all
- remote-header-name
@ -56,6 +57,10 @@ Or for Windows:
curl example.com -o nul
Or, even more efficient and portable, use
curl example.com --out-null
Specify the filename as single minus to force the output to stdout, to
override curl's internal binary output in terminal prevention:

View File

@ -145,6 +145,7 @@
--ntlm 7.10.6
--ntlm-wb 7.22.0
--oauth2-bearer 7.33.0
--out-null 8.16.0
--output (-o) 4.0
--output-dir 7.73.0
--parallel (-Z) 7.66.0

View File

@ -141,6 +141,9 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
intptr_t fhnd;
#endif
if(outs->out_null)
return bytes;
#ifdef DEBUGBUILD
{
char *tty = curl_getenv("CURL_ISATTY");

View File

@ -224,6 +224,7 @@ static const struct LongShort aliases[]= {
{"ntlm", ARG_BOOL, ' ', C_NTLM},
{"ntlm-wb", ARG_BOOL|ARG_DEPR, ' ', C_NTLM_WB},
{"oauth2-bearer", ARG_STRG|ARG_CLEAR, ' ', C_OAUTH2_BEARER},
{"out-null", ARG_BOOL, ' ', C_OUT_NULL},
{"output", ARG_FILE, 'o', C_OUTPUT},
{"output-dir", ARG_STRG, ' ', C_OUTPUT_DIR},
{"parallel", ARG_BOOL, 'Z', C_PARALLEL},
@ -1310,9 +1311,13 @@ static ParameterError parse_output(struct OperationConfig *config,
return PARAM_NO_MEM;
/* fill in the outfile */
err = getstr(&url->outfile, nextarg, DENY_BLANK);
if(nextarg)
err = getstr(&url->outfile, nextarg, DENY_BLANK);
else
url->outfile = NULL;
url->useremote = FALSE; /* switch off */
url->outset = TRUE;
url->out_null = !nextarg;
return err;
}
@ -1351,6 +1356,7 @@ static ParameterError parse_remote_name(struct OperationConfig *config,
url->outfile = NULL; /* leave it */
url->useremote = toggle;
url->outset = TRUE;
url->out_null = FALSE;
return PARAM_OK;
}
@ -1833,6 +1839,8 @@ static ParameterError opt_bool(struct OperationConfig *config,
return PARAM_LIBCURL_DOESNT_SUPPORT;
togglebit(toggle, &config->authtype, CURLAUTH_NTLM);
break;
case C_OUT_NULL: /* --out-null */
return parse_output(config, NULL);
case C_BASIC: /* --basic */
togglebit(toggle, &config->authtype, CURLAUTH_BASIC);
break;

View File

@ -167,6 +167,7 @@ typedef enum {
C_NTLM,
C_NTLM_WB,
C_OAUTH2_BEARER,
C_OUT_NULL,
C_OUTPUT,
C_OUTPUT_DIR,
C_PARALLEL,

View File

@ -442,6 +442,9 @@ const struct helptxt helptext[] = {
"OAuth 2 Bearer Token",
CURLHELP_AUTH | CURLHELP_IMAP | CURLHELP_POP3 | CURLHELP_SMTP |
CURLHELP_LDAP},
{" --out-null",
"Discard response data into the void",
CURLHELP_OUTPUT},
{"-o, --output <file>",
"Write to file instead of stdout",
CURLHELP_IMPORTANT | CURLHELP_OUTPUT},

View File

@ -1321,7 +1321,8 @@ static CURLcode single_transfer(struct OperationConfig *config,
}
}
if((urlnode->useremote ||
outs->out_null = urlnode->out_null;
if(!outs->out_null && (urlnode->useremote ||
(per->outfile && strcmp("-", per->outfile)))) {
result = setup_outfile(config, per, outs, skipped);
if(result)

View File

@ -74,6 +74,7 @@ struct OutStruct {
BIT(is_cd_filename);
BIT(s_isreg);
BIT(fopened);
BIT(out_null);
};
/*
@ -95,6 +96,7 @@ struct getout {
BIT(useremote); /* use remote filename locally */
BIT(noupload); /* if set, -T "" has been used */
BIT(noglob); /* disable globbing for this URL */
BIT(out_null); /* discard output for this URL */
};
/*
* 'trace' enumeration represents curl's output look'n feel possibilities.

View File

@ -108,7 +108,7 @@ test718 test719 test720 test721 test722 test723 test724 test725 test726 \
test727 test728 test729 test730 test731 test732 test733 test734 test735 \
test736 test737 test738 test739 test740 test741 test742 test743 test744 \
test745 test746 test747 test748 test749 test750 test751 test752 test753 \
test754 test755 \
test754 test755 test756 \
test780 test781 test782 test783 test784 test785 test786 test787 test788 \
test789 test790 test791 test792 test793 \
\

80
tests/data/test756 Normal file
View File

@ -0,0 +1,80 @@
<testcase>
<info>
<keywords>
HTTP
</keywords>
</info>
# Server-side
<reply>
<data nocheck="yes">
HTTP/1.1 200 OK
Date: Thu, 09 Nov 2010 14:49:00 GMT
Server: test-server/fake
Response: 1
Content-Type: text/html
Content-Length: 8
Hello1!
</data>
<data2>
HTTP/1.1 200 OK
Date: Thu, 09 Nov 2010 14:49:00 GMT
Server: test-server/fake
Response: 2
Content-Type: text/html
Content-Length: 8
Hello2!
</data2>
</reply>
# Client-side
<client>
<server>
http
</server>
<features>
</features>
<name>
mix --output and --out-null
</name>
<command>
http://%HOSTIP:%HTTPPORT/want/%TESTNUMBER http://%HOSTIP:%HTTPPORT/want/%TESTNUMBER0002 --out-null -o -
</command>
</client>
# Verify data after the test has been "shot"
<verify>
<protocol>
GET /want/%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
GET /want/%TESTNUMBER0002 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
</protocol>
<stdout>
HTTP/1.1 200 OK
Date: Thu, 09 Nov 2010 14:49:00 GMT
Server: test-server/fake
Response: 1
Content-Type: text/html
Content-Length: 8
HTTP/1.1 200 OK
Date: Thu, 09 Nov 2010 14:49:00 GMT
Server: test-server/fake
Response: 2
Content-Type: text/html
Content-Length: 8
Hello2!
</stdout>
</verify>
</testcase>

View File

@ -587,7 +587,7 @@ class CurlClient:
extra_args = []
if no_save:
extra_args.extend([
'-o', '/dev/null',
'--out-null',
])
else:
extra_args.extend([
@ -636,7 +636,7 @@ class CurlClient:
if extra_args is None:
extra_args = []
extra_args.extend([
'-X', 'DELETE', '-o', '/dev/null',
'-X', 'DELETE', '--out-null',
])
if with_stats:
extra_args.extend([
@ -702,7 +702,7 @@ class CurlClient:
extra_args = []
if no_save:
extra_args.extend([
'-o', '/dev/null',
'--out-null',
])
else:
extra_args.extend([