Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to word split on newline only

19 views
Skip to first unread message

Kenny McCormack

unread,
May 16, 2017, 4:25:22 AM5/16/17
to
I want to do something like:

$ set -- $(printf "This is a test\nThis is line 2\nThis is line 3\n")

and have $# = 3 and
$1 = This is a test
$2 = This is line 2
$3 = This is line 3

Unfortunately, it splits on spaces and you end up with $# some large number
and lots of little strings.

I think the solution has something to do with IFS, but I am unable to get
it working.

--
The randomly chosen signature file that would have appeared here is more than 4
lines long. As such, it violates one or more Usenet RFCs. In order to remain
in compliance with said RFCs, the actual sig can be found at the following URL:
http://user.xmission.com/~gazelle/Sigs/Seneca

Janis Papanagnou

unread,
May 16, 2017, 6:01:17 AM5/16/17
to
On 16.05.2017 10:25, Kenny McCormack wrote:
> I want to do something like:
>
> $ set -- $(printf "This is a test\nThis is line 2\nThis is line 3\n")
>
> and have $# = 3 and
> $1 = This is a test
> $2 = This is line 2
> $3 = This is line 3
>
> Unfortunately, it splits on spaces and you end up with $# some large number
> and lots of little strings.
>
> I think the solution has something to do with IFS, but I am unable to get
> it working.

Exactly.

IFS=$'\n'
set -- $(printf "This is a test\nThis is line 2\nThis is line 3\n")
printf "'%s'\n" "$@"

Output:
'This is a test'
'This is line 2'
'This is line 3'


Janis

Christian Weisgerber

unread,
May 16, 2017, 8:30:09 AM5/16/17
to
On 2017-05-16, Kenny McCormack <gaz...@shell.xmission.com> wrote:

> I want to do something like:
>
> $ set -- $(printf "This is a test\nThis is line 2\nThis is line 3\n")
>
> Unfortunately, it splits on spaces and you end up with $# some large number
> and lots of little strings.
>
> I think the solution has something to do with IFS, but I am unable to get
> it working.

All you need to do is set IFS to newline. That's easiest by using
an actual newline:

IFS='
'

--
Christian "naddy" Weisgerber na...@mips.inka.de

Kenny McCormack

unread,
May 16, 2017, 9:45:55 AM5/16/17
to
In article <ofeila$hkn$1...@news-1.m-online.net>,
Janis Papanagnou <janis_pa...@hotmail.com> wrote:
>On 16.05.2017 10:25, Kenny McCormack wrote:
>> I want to do something like:
>>
>> $ set -- $(printf "This is a test\nThis is line 2\nThis is line 3\n")
>>
>> and have $# = 3 and
>> $1 = This is a test
>> $2 = This is line 2
>> $3 = This is line 3
>>
>> Unfortunately, it splits on spaces and you end up with $# some large number
>> and lots of little strings.
>>
>> I think the solution has something to do with IFS, but I am unable to get
>> it working.
>
>Exactly.
>
>IFS=$'\n'
>set -- $(printf "This is a test\nThis is line 2\nThis is line 3\n")
>printf "'%s'\n" "$@"

OK - thanks - that works. I didn't know (or had forgotten) the $'\n'
trick. I had been doing it with just IFS="\n", which seems to be a no-op.

Now, the next case:

$ history |wc
500 4614 27659
$ IFS=$'\n' set -- $(history);echo $#
4661

I've tried various variations of this, but it always ends up splitting on
spaces (verfied by printing out, say, $4000 in the above example). How to
fix?

--
(Cruz certainly has an odd face) ... it looks like someone sewed pieces of a
waterlogged Reagan mask together at gunpoint ...

http://www.rollingstone.com/politics/news/how-america-made-donald-trump-unstoppable-20160224

Janis Papanagnou

unread,
May 16, 2017, 10:21:31 AM5/16/17
to
(Note that because 'set' is a special builtin where the environment
for the command can't be set directly. (Frankly, I don't know why it
can't be handled consistently but that's what we have to live with.)
Have you tried putting the IFS=... on a separate line as I suggested?

Janis

Kenny McCormack

unread,
May 16, 2017, 11:33:11 AM5/16/17
to
In article <off1t5$m2e$1...@news-1.m-online.net>,
Janis Papanagnou <janis_pa...@hotmail.com> wrote:
...
>(Note that because 'set' is a special builtin where the environment
>for the command can't be set directly. (Frankly, I don't know why it
>can't be handled consistently but that's what we have to live with.)
>Have you tried putting the IFS=... on a separate line as I suggested?

I hadn't, but, yes, this works:

$ IFS=$'\n';set -- $(history);echo $#
500
$

Of course, what's messy about that is you now have to deal with
saving/restoring the old IFS value (the above leaves IFS set to just
newline). The good news, though, is that this doesn't break your shell
like it did in the old days.

It's weird, though, because I'm pretty sure that I've seen code like that
(without the semicolon before the 'set') in posts and stuff, but now it
seems like that can't be right. Oh well...

--
The randomly chosen signature file that would have appeared here is more than 4
lines long. As such, it violates one or more Usenet RFCs. In order to remain
in compliance with said RFCs, the actual sig can be found at the following URL:
http://user.xmission.com/~gazelle/Sigs/Aspergers

Barry Margolin

unread,
May 16, 2017, 11:33:51 AM5/16/17
to
In article <ofevqe$nhq$1...@news.xmission.com>,
gaz...@shell.xmission.com (Kenny McCormack) wrote:

> In article <ofeila$hkn$1...@news-1.m-online.net>,
> Janis Papanagnou <janis_pa...@hotmail.com> wrote:
> >On 16.05.2017 10:25, Kenny McCormack wrote:
> >> I want to do something like:
> >>
> >> $ set -- $(printf "This is a test\nThis is line 2\nThis is line 3\n")
> >>
> >> and have $# = 3 and
> >> $1 = This is a test
> >> $2 = This is line 2
> >> $3 = This is line 3
> >>
> >> Unfortunately, it splits on spaces and you end up with $# some large number
> >> and lots of little strings.
> >>
> >> I think the solution has something to do with IFS, but I am unable to get
> >> it working.
> >
> >Exactly.
> >
> >IFS=$'\n'
> >set -- $(printf "This is a test\nThis is line 2\nThis is line 3\n")
> >printf "'%s'\n" "$@"
>
> OK - thanks - that works. I didn't know (or had forgotten) the $'\n'
> trick. I had been doing it with just IFS="\n", which seems to be a no-op.

It's not really a no-op. Try:

var="foo\barnbaz"
IFS="\n"
printf '|%s|\n' $var

\ and n are now the field separators.

--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***

Ed Morton

unread,
May 16, 2017, 11:57:57 AM5/16/17
to
On 5/16/2017 10:33 AM, Kenny McCormack wrote:
> In article <off1t5$m2e$1...@news-1.m-online.net>,
> Janis Papanagnou <janis_pa...@hotmail.com> wrote:
> ...
>> (Note that because 'set' is a special builtin where the environment
>> for the command can't be set directly. (Frankly, I don't know why it
>> can't be handled consistently but that's what we have to live with.)
>> Have you tried putting the IFS=... on a separate line as I suggested?
>
> I hadn't, but, yes, this works:
>
> $ IFS=$'\n';set -- $(history);echo $#
> 500
> $
>
> Of course, what's messy about that is you now have to deal with
> saving/restoring the old IFS value (the above leaves IFS set to just
> newline). The good news, though, is that this doesn't break your shell
> like it did in the old days.
>
> It's weird, though, because I'm pretty sure that I've seen code like that
> (without the semicolon before the 'set') in posts and stuff, but now it
> seems like that can't be right. Oh well...
>

IFS=$'\n' command

makes the value of IFS (or any other variable set on the command line) available
in the environment **during the execution of command**. It does not make it
available during parameter expansion for command so in particular it's not
available when command is "set -- parameters".

Use this instead:

$ IFS=$'\n' read -r -d '' -a arr < <(printf '1 2\n3\n' && printf '\0'); declare
-p arr
declare -a arr=([0]="1 2" [1]="3")

and if you MUST set positional parameters after that (can't imagine why you
wouldn't just use the array though) then set them from the array contents:

$ set -- "${arr[@]}"; echo "$#: $1, $2"
2: 1 2, 3

Replace "printf '1 2\n3\n'" with "history" or whatever command you like of course.

Regards,

Ed.

Ed Morton

unread,
May 16, 2017, 12:04:43 PM5/16/17
to
For example:

$ x=7 awk 'BEGIN{print "<" ENVIRON["x"] ">"}'
<7>

$ x=7 awk 'BEGIN{print "<" ARGV[1] ">"}' "$x"
<>

$ x=7 awk -v x="$x" 'BEGIN{print "<" x ">"}'
0 new messages