Hello, my apologies if there's a much easier solution for the following problem - in this case please let me know!
>From time to time a run into troubles when reading a file with a while-read >loop where the last "line" is not terminated with a newline. I found an ugly looking solution (probably relying on undocumented features) when reading the whole line into one variable (see below). The attached patch for bash-5.1.8 will add an -E option to the builtin read that will avoid the problem. To test it run the patched bash on the following script: >>> input=$'Line 1\nLine 2\nIncomplete line 3' echo "while read line" printf '%s' "$input" | while read line; do printf ' %s\n' "$line"; done echo "while read line || [[ \$line != '' ]]" printf '%s' "$input" | while read line || [[ $line != '' ]]; do printf ' %s\n' "$line"; done echo "while read -E line" printf '%s' "$input" | while read -E line; do printf ' %s\n' "$line"; done echo "while read -E line with no characters between last \\n and EOF" printf '%s\n' "$input" | sed 's/Incomplete l/L/' | while read -E line; do printf ' %s\n' "$line"; done <<< The patch has not been tested intensely - first I would like to hear if I'm on a sensible way. Best regards Martin
--- ../bash-5.1.8-ori/builtins/read.def 2020-06-05 19:18:28.000000000 +0200 +++ builtins/read.def 2021-10-23 21:23:37.067915781 +0200 @@ -22,7 +22,7 @@ $BUILTIN read $FUNCTION read_builtin -$SHORT_DOC read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...] +$SHORT_DOC read [-eErs] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...] Read a line from the standard input and split it into fields. Reads a single line from the standard input, or from file descriptor FD @@ -40,6 +40,7 @@ -d delim continue until the first character of DELIM is read, rather than newline -e use Readline to obtain the line + -E return text between last newline and EOF -i text use TEXT as the initial text for Readline -n nchars return after reading NCHARS characters rather than waiting for a newline, but honor a delimiter if fewer than @@ -179,7 +180,7 @@ int size, nr, pass_next, saw_escape, eof, opt, retval, code, print_ps2, nflag; volatile int i; int input_is_tty, input_is_pipe, unbuffered_read, skip_ctlesc, skip_ctlnul; - int raw, edit, nchars, silent, have_timeout, ignore_delim, fd; + int raw, edit, eof_terminates_line, nchars, silent, have_timeout, ignore_delim, fd; int lastsig, t_errno; int mb_cur_max; unsigned int tmsec, tmusec; @@ -209,6 +210,7 @@ USE_VAR(input_is_pipe); /* USE_VAR(raw); */ USE_VAR(edit); + USE_VAR(eof_terminates_line); USE_VAR(tmsec); USE_VAR(tmusec); USE_VAR(nchars); @@ -229,6 +231,7 @@ i = 0; /* Index into the string that we are reading. */ raw = edit = 0; /* Not reading raw input by default. */ + eof_terminates_line = 0; silent = 0; arrayname = prompt = (char *)NULL; fd = 0; /* file descriptor to read from */ @@ -245,7 +248,7 @@ ignore_delim = nflag = 0; reset_internal_getopt (); - while ((opt = internal_getopt (list, "ersa:d:i:n:p:t:u:N:")) != -1) + while ((opt = internal_getopt (list, "eErsa:d:i:n:p:t:u:N:")) != -1) { switch (opt) { @@ -263,6 +266,9 @@ edit = 1; #endif break; + case 'E': + eof_terminates_line = 1; + break; case 'i': #if defined (READLINE) itext = list_optarg; @@ -788,7 +794,14 @@ discard_unwind_frame ("read_builtin"); - retval = eof ? EXECUTION_FAILURE : EXECUTION_SUCCESS; + if (!eof_terminates_line) + { + retval = eof ? EXECUTION_FAILURE : EXECUTION_SUCCESS; + } + else + { + retval = eof && strlen (input_string) == 0 ? EXECUTION_FAILURE : EXECUTION_SUCCESS; + } assign_vars: