From 06afcb43b4aa2674c04297cf3b4bd9c9c6a3f801 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 16 Sep 2017 13:50:47 -0500 Subject: [PATCH] Support reading to stdout via builtin `read -` Added an option to read to stdout via `read -`. While it may seem useless at first blush, it lets you do things like include mysql -p(read --silent) ... Without needing to save to a local variable and then echo it back. Kicks in when `-` is provided as the variable name to read to. This is in keeping with the de facto syntax for reading/writing from/to stdin/stdout instead of a file in, e.g., tar, cat, and other standard unix utilities. --- src/builtin_read.cpp | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/builtin_read.cpp b/src/builtin_read.cpp index 6f75537d9..4361f1801 100644 --- a/src/builtin_read.cpp +++ b/src/builtin_read.cpp @@ -2,6 +2,7 @@ #include "config.h" // IWYU pragma: keep #include +#include #include #include #include @@ -374,6 +375,10 @@ static int validate_read_args(const wchar_t *cmd, read_cmd_opts_t &opts, int arg // Verify all variable names. for (int i = 0; i < argc; i++) { if (!valid_var_name(argv[i])) { + if (wcscmp(L"-", argv[i]) == 0) { + //writing to stdout instead + continue; + } streams.err.append_format(BUILTIN_ERR_VARNAME, cmd, argv[i]); builtin_print_help(parser, streams, cmd, streams.err); return STATUS_INVALID_ARGS; @@ -383,6 +388,26 @@ static int validate_read_args(const wchar_t *cmd, read_cmd_opts_t &opts, int arg return STATUS_CMD_OK; } +void print_or_set(const wcstring &key, env_mode_flags_t mode, wcstring_list_t vals) { + if (key == L"-") { + for (const auto &val : vals) { + std::wcout << val << std::endl; + } + } + else { + env_set(key, mode, vals); + } +} + +void print_or_set_one(const wcstring &key, env_mode_flags_t mode, wcstring val) { + if (key == L"-") { + std::wcout << val << std::endl; + } + else { + env_set_one(key, mode, val); + } +} + /// The read builtin. Reads from stdin and stores the values in environment variables. int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wchar_t *cmd = argv[0]; @@ -455,7 +480,7 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (opts.array) { // Array mode: assign each char as a separate element of the sole var. - env_set(argv[0], opts.place, chars); + print_or_set(argv[0], opts.place, chars); } else { // Not array mode: assign each char to a separate var with the remainder being assigned // to the last var. @@ -463,16 +488,16 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { size_t j = 0; for (; i + 1 < argc; ++i) { if (j < chars.size()) { - env_set_one(argv[i], opts.place, chars[j]); + print_or_set_one(argv[i], opts.place, chars[j]); j++; } else { - env_set_one(argv[i], opts.place, L""); + print_or_set_one(argv[i], opts.place, L""); } } if (i < argc) { wcstring val = chars.size() == static_cast(argc) ? chars[i] : L""; - env_set_one(argv[i], opts.place, val); + print_or_set_one(argv[i], opts.place, val); } else { env_set_empty(argv[i], opts.place); } @@ -490,13 +515,13 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { loc.first != wcstring::npos; loc = wcstring_tok(buff, opts.delimiter, loc)) { tokens.emplace_back(wcstring(buff, loc.first, loc.second)); } - env_set(argv[0], opts.place, tokens); + print_or_set(argv[0], opts.place, tokens); } else { // We're using a delimiter provided by the user so use the `string split` behavior. wcstring_list_t splits; split_about(buff.begin(), buff.end(), opts.delimiter.begin(), opts.delimiter.end(), &splits, LONG_MAX); - env_set(argv[0], opts.place, splits); + print_or_set(argv[0], opts.place, splits); } } else { // Not array mode. Split the input into tokens and assign each to the vars in sequence. @@ -510,7 +535,7 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (loc.first != wcstring::npos) { substr = wcstring(buff, loc.first, loc.second); } - env_set_one(argv[i], opts.place, substr); + print_or_set_one(argv[i], opts.place, substr); } } else { // We're using a delimiter provided by the user so use the `string split` behavior. @@ -520,7 +545,7 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { split_about(buff.begin(), buff.end(), opts.delimiter.begin(), opts.delimiter.end(), &splits, argc - 1); for (size_t i = 0; i < (size_t)argc && i < splits.size(); i++) { - env_set_one(argv[i], opts.place, splits[i]); + print_or_set_one(argv[i], opts.place, splits[i]); } } }