Bug 193759 - Wrong behaviour of IFS='|' set in /bin/sh
Summary: Wrong behaviour of IFS='|' set in /bin/sh
Status: Closed Works As Intended
Alias: None
Product: Base System
Classification: Unclassified
Component: bin (show other bugs)
Version: 10.0-RELEASE
Hardware: Any Any
: --- Affects Only Me
Assignee: Jilles Tjoelker
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-09-18 21:11 UTC by Michael Grünewald
Modified: 2015-01-04 19:01 UTC (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Michael Grünewald 2014-09-18 21:11:09 UTC
Using IFS to set positional parameters as in

  IFS='|' set -- $line

displays erratic behaviour.  In the script below, the expected output is

  A-1-2
  B-1-2
  C-1-2

but the first splitting is handled specially and we get

  A|1|2--
  B-1-2
  C-1-2

----8<----
mock()
{
    cat <<EOF
A|1|2
B|1|2
C|1|2
EOF
}

demonstrate()
{
    while read line; do
	IFS='|' set -- $line
	printf '%s-%s-%s\n' "$1" "$2" "$3"
    done
}

mock | demonstrate
---->8----

Note that splitting twice fixes the output.

----8<----
demonstrate2()
{
    while read line; do
        # The following line has been intentionally duplicated
	IFS='|' set -- $line
	IFS='|' set -- $line
	printf '%s-%s-%s\n' "$1" "$2" "$3"
    done
}

mock | demonstrate2
---->8----
Comment 1 Jilles Tjoelker freebsd_committer 2014-09-18 21:37:04 UTC
Thanks for your report. However, the behaviour is as designed and matches the POSIX.1-2008 standard.

Firstly, assignments are performed after the command is expanded. Therefore, an IFS= assignment preceding a command never affects word splitting of that command itself.

Secondly, an assignment preceding a special built-in utility such as 'set' remains in effect after the command completes. This explains why the second and later commands work.

You need to set IFS before word splitting and possibly restore it later, or use IFS=... read ... (where the assignment does affect read's splitting and does not persist after the command completes).
Comment 2 Michael Grünewald 2014-09-18 22:03:15 UTC
Thank for your detailed and precise answer,

> You need to set IFS before word splitting and possibly restore it later, or use IFS=... read ... (where the assignment does affect read's splitting and does not persist after the command completes).

If I understand correctly, the form IFS=… read … is a handy exception to the general rule?
Comment 3 commit-hook freebsd_committer 2014-09-21 20:35:11 UTC
A commit references this bug:

Author: jilles
Date: Sun Sep 21 20:34:54 UTC 2014
New revision: 271936
URL: http://svnweb.freebsd.org/changeset/base/271936

Log:
  sh(1): Clarify that assignments before commands do not affect expansions.

  PR:		193759
  MFC after:	1 week

Changes:
  head/bin/sh/sh.1
Comment 4 Jilles Tjoelker freebsd_committer 2014-09-21 20:38:21 UTC
(In reply to Michael Grünewald from comment #2)
> Thank for your detailed and precise answer,

I clarified the man page sh(1) a little.

> If I understand correctly, the form IFS=… read … is a handy exception to the
> general rule?

read's splitting is not a word expansion but part of read's operation proper.
Comment 5 Michael Grünewald 2014-09-21 22:59:35 UTC
Thank you very much for your very precise answers!
Comment 6 commit-hook freebsd_committer 2015-01-04 18:59:22 UTC
A commit references this bug:

Author: jilles
Date: Sun Jan  4 18:58:52 UTC 2015
New revision: 276661
URL: https://svnweb.freebsd.org/changeset/base/276661

Log:
  MFC r271936: sh(1): Clarify that assignments before commands do not affect
  expansions.

  PR:		193759

Changes:
_U  stable/10/
  stable/10/bin/sh/sh.1
Comment 7 commit-hook freebsd_committer 2015-01-04 19:01:23 UTC
A commit references this bug:

Author: jilles
Date: Sun Jan  4 19:00:41 UTC 2015
New revision: 276662
URL: https://svnweb.freebsd.org/changeset/base/276662

Log:
  MFC r271936: sh(1): Clarify that assignments before commands do not affect
  expansions.

  PR:		193759

Changes:
_U  stable/9/bin/sh/
  stable/9/bin/sh/sh.1