Hi, as has been reported already dash currently has a bug where the read builtin ignores the read environment's IFS setting. As a result, echo a:b | { IFS=: read a b; echo $a; } will write out a:b. I tried to see what changed between 0.5.5.1 and 0.5.6, and found that the old code used bltinlookup("IFS"). So, I made the new code also use that. The above example works properly with the attached patch. However, testing further, I found that there is bad interaction (even without my patch, where the IFS assignment below should just be ignored) between the code that handles variable assignments, and read. Try this: $ src/dash -c 'printf "a\t\tb\n" | { IFS=$(printf "\t") read a b c; echo "|$a|$b|$c|"; }' |a||b| $ src/dash -c 'printf "a\t\tb\n" | { IFS="$(printf "\t")" read a b c; echo "|$a|$b|$c|"; }' |a|b|| This happens because expbackq is correctly called without EXP_QUOTED, which makes it call recordregion, which isn't cleared by the time read calls ifsbreakup. I'm not sure how that should be solved, and if there are more cases where it would fail. The simplest way to solve this for read is to call removerecordregions(0) before recordregion(0, len - 1, 0). I have tested that locally, and it works. I am not familiar enough with the code to judge whether the same situation can also happen in other cases that would also need fixing, which is why I have not included that part in the attached patch. Cheers, Harald (Re-sent because of local mail problems.)
diff --git a/src/expand.c b/src/expand.c index f2f964c..3ba1a38 100644 --- a/src/expand.c +++ b/src/expand.c @@ -205,7 +205,7 @@ expandarg(union node *arg, struct arglist *arglist, int flag) * TODO - EXP_REDIR */ if (flag & EXP_FULL) { - ifsbreakup(p, &exparg); + ifsbreakup(p, &exparg, 0); *exparg.lastp = NULL; exparg.lastp = &exparg.list; expandmeta(exparg.list, flag); @@ -1022,9 +1022,11 @@ recordregion(int start, int end, int nulonly) * Break the argument string into pieces based upon IFS and add the * strings to the argument list. The regions of the string to be * searched for IFS characters have been stored by recordregion. + * If bltin is set, use bltinlookup to search for IFS in the + * environment of the currently executing built-in command. */ void -ifsbreakup(char *string, struct arglist *arglist) +ifsbreakup(char *string, struct arglist *arglist, int bltin) { struct ifsregion *ifsp; struct strlist *sp; @@ -1040,7 +1042,13 @@ ifsbreakup(char *string, struct arglist *arglist) if (ifslastp != NULL) { ifsspc = 0; nulonly = 0; - realifs = ifsset() ? ifsval() : defifs; + if (!bltin) + realifs = ifsset() ? ifsval() : defifs; + else { + realifs = bltinlookup("IFS"); + if (realifs == NULL) + realifs = defifs; + } ifsp = &ifsfirst; do { p = string + ifsp->begoff; diff --git a/src/expand.h b/src/expand.h index 405af0b..8eb5f07 100644 --- a/src/expand.h +++ b/src/expand.h @@ -69,7 +69,7 @@ char *_rmescapes(char *, int); int casematch(union node *, char *); void recordregion(int, int, int); void removerecordregions(int); -void ifsbreakup(char *, struct arglist *); +void ifsbreakup(char *, struct arglist *, int bltin); /* From arith.y */ intmax_t arith(const char *); diff --git a/src/miscbltin.c b/src/miscbltin.c index 5ab1648..898ab09 100644 --- a/src/miscbltin.c +++ b/src/miscbltin.c @@ -87,7 +87,7 @@ readcmd_handle_line(char *line, char **ap, size_t len) arglist.lastp = &arglist.list; recordregion(0, len - 1, 0); - ifsbreakup(s, &arglist); + ifsbreakup(s, &arglist, 1); *arglist.lastp = NULL; removerecordregions(0);