Skip to content

Commit 67ee2c4

Browse files
committed
feat: allow writing to output file (close #2418)
This allows to write the output to a file and introduces the options -o and --output-file that take a filename as parameter. When not specifying -o, stdout will be used for compatibility. This will be helpful when calling jq inside a docker context as it means jq will not have to be called from within a shell with output redirection.
1 parent 3c5ceac commit 67ee2c4

File tree

2 files changed

+33
-13
lines changed

2 files changed

+33
-13
lines changed

src/jv_print.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ static const char *colors[] = DEFAULT_COLORS;
3535
#define COLORS_LEN (sizeof(colors) / sizeof(colors[0]))
3636
#define FIELD_COLOR (colors[7])
3737

38+
extern FILE *ofile;
39+
3840
static char *colors_buf = NULL;
3941
int jq_set_colors(const char *code_str) {
4042
if (code_str == NULL)
@@ -390,7 +392,7 @@ void jv_dumpf(jv x, FILE *f, int flags) {
390392
}
391393

392394
void jv_dump(jv x, int flags) {
393-
jv_dumpf(x, stdout, flags);
395+
jv_dumpf(x, ofile, flags);
394396
}
395397

396398
/* This one is nice for use in debuggers */

src/main.c

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ extern void jv_tsd_dtoa_ctx_init();
4242

4343
int jq_testsuite(jv lib_dirs, int verbose, int argc, char* argv[]);
4444

45+
FILE *ofile;
46+
4547
/*
4648
* For a longer help message we could use a better option parsing
4749
* strategy, one that lets stack options.
@@ -81,6 +83,7 @@ static void usage(int code, int keep_it_short) {
8183
" each output;\n"
8284
" -a, --ascii-output output strings by only ASCII characters\n"
8385
" using escape sequences;\n"
86+
" -o, --output-file output to file instead of stdout\n"
8487
" -S, --sort-keys sort keys of each object on output;\n"
8588
" -C, --color-output colorize JSON output;\n"
8689
" -M, --monochrome-output disable colored output;\n"
@@ -179,15 +182,15 @@ static int process(jq_state *jq, jv value, int flags, int dumpopts, int options)
179182
while (jv_is_valid(result = jq_next(jq))) {
180183
if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) {
181184
if (options & ASCII_OUTPUT) {
182-
jv_dumpf(jv_copy(result), stdout, JV_PRINT_ASCII);
185+
jv_dumpf(jv_copy(result), ofile, JV_PRINT_ASCII);
183186
} else if ((options & RAW_OUTPUT0) && strlen(jv_string_value(result)) != (unsigned long)jv_string_length_bytes(jv_copy(result))) {
184187
jv_free(result);
185188
result = jv_invalid_with_msg(jv_string(
186189
"Cannot dump a string containing NUL with --raw-output0 option"));
187190
break;
188191
} else {
189192
priv_fwrite(jv_string_value(result), jv_string_length_bytes(jv_copy(result)),
190-
stdout, dumpopts & JV_PRINT_ISATTY);
193+
ofile, dumpopts & JV_PRINT_ISATTY);
191194
}
192195
ret = JQ_OK;
193196
jv_free(result);
@@ -197,15 +200,15 @@ static int process(jq_state *jq, jv value, int flags, int dumpopts, int options)
197200
else
198201
ret = JQ_OK;
199202
if (options & SEQ)
200-
priv_fwrite("\036", 1, stdout, dumpopts & JV_PRINT_ISATTY);
203+
priv_fwrite("\036", 1, ofile, dumpopts & JV_PRINT_ISATTY);
201204
jv_dump(result, dumpopts);
202205
}
203206
if (!(options & RAW_NO_LF))
204-
priv_fwrite("\n", 1, stdout, dumpopts & JV_PRINT_ISATTY);
207+
priv_fwrite("\n", 1, ofile, dumpopts & JV_PRINT_ISATTY);
205208
if (options & RAW_OUTPUT0)
206-
priv_fwrite("\0", 1, stdout, dumpopts & JV_PRINT_ISATTY);
209+
priv_fwrite("\0", 1, ofile, dumpopts & JV_PRINT_ISATTY);
207210
if (options & UNBUFFERED_OUTPUT)
208-
fflush(stdout);
211+
fflush(ofile);
209212
}
210213
if (jq_halted(jq)) {
211214
// jq program invoked `halt` or `halt_error`
@@ -288,6 +291,7 @@ int umain(int argc, char* argv[]) {
288291
#else /*}*/
289292
int main(int argc, char* argv[]) {
290293
#endif
294+
ofile = stdout;
291295
jq_state *jq = NULL;
292296
jq_util_input_state *input_state = NULL;
293297
int ret = JQ_OK_NO_OUTPUT;
@@ -318,9 +322,9 @@ int main(int argc, char* argv[]) {
318322

319323
#ifdef WIN32
320324
jv_tsd_dtoa_ctx_init();
321-
fflush(stdout);
325+
fflush(ofile);
322326
fflush(stderr);
323-
_setmode(fileno(stdout), _O_TEXT | _O_U8TEXT);
327+
_setmode(fileno(ofile), _O_TEXT | _O_U8TEXT);
324328
_setmode(fileno(stderr), _O_TEXT | _O_U8TEXT);
325329
#endif
326330

@@ -419,10 +423,10 @@ int main(int argc, char* argv[]) {
419423
}
420424
} else if (isoption(&text, 'b', "binary", is_short)) {
421425
#ifdef WIN32
422-
fflush(stdout);
426+
fflush(ofile);
423427
fflush(stderr);
424428
_setmode(fileno(stdin), _O_BINARY);
425-
_setmode(fileno(stdout), _O_BINARY);
429+
_setmode(fileno(ofile), _O_BINARY);
426430
_setmode(fileno(stderr), _O_BINARY);
427431
#endif
428432
} else if (isoption(&text, 0, "tab", is_short)) {
@@ -480,6 +484,20 @@ int main(int argc, char* argv[]) {
480484
program_arguments = jv_object_set(program_arguments, jv_string(argv[i+1]), v);
481485
}
482486
i += 2; // skip the next two arguments
487+
} else if (isoption(&text, 'o', "output-file", is_short)) {
488+
const char *which = "output-file";
489+
if (i >= argc - 1) {
490+
fprintf(stderr, "jq: --%s takes one parameter (e.g. --%s filename)\n", which, which);
491+
die();
492+
}
493+
494+
ofile = fopen(argv[i+1], "w");
495+
if (!ofile ) {
496+
fprintf(stderr, "jq: unable to open output-file.");
497+
die();
498+
}
499+
500+
i += 1; // skip the next argument
483501
} else if ((raw = isoption(&text, 0, "rawfile", is_short)) ||
484502
isoption(&text, 0, "slurpfile", is_short)) {
485503
const char *which = raw ? "rawfile" : "slurpfile";
@@ -696,8 +714,8 @@ int main(int argc, char* argv[]) {
696714
ret = JQ_ERROR_SYSTEM;
697715

698716
out:
699-
badwrite = ferror(stdout);
700-
if (fclose(stdout)!=0 || badwrite) {
717+
badwrite = ferror(ofile);
718+
if (fclose(ofile)!=0 || badwrite) {
701719
fprintf(stderr,"jq: error: writing output failed: %s\n", strerror(errno));
702720
ret = JQ_ERROR_SYSTEM;
703721
}

0 commit comments

Comments
 (0)