You are not logged in.

#1 2013-01-30 19:02:04

bslackr
Member
Registered: 2012-01-27
Posts: 131

[solved]Why does this not work in bash?

This script works for me in zsh, but not in bash and I'm not sure why. Any help would be appreciated.

examples=("apple|one|core" "trash|2|dummy")

for i in ${examples[@]}; do
    echo "$i" | IFS="|" read a b c
    echo $a                            
    echo $c
done                      

zsh output:

>apple
>core
>trash
>dummy

Bash only outputs newlines. I am trying to achieve what zsh is doing in bash.

Last edited by bslackr (2013-01-30 22:53:04)

Offline

#2 2013-01-30 19:32:20

Xyne
Administrator/PM
Registered: 2008-08-03
Posts: 6,963
Website

Re: [solved]Why does this not work in bash?

I think it is due to unexpected behavior in variable scoping when using pipes. I've run into this before but I don't remember the exact explanation.

The following works in Bash:

examples=("apple|one|core" "trash|2|dummy")

for i in "${examples[@]}"; do
    IFS="|" read a b c < <(echo "$i")
    echo $a
    echo $c
done

Explanation:
Bash replaces "<(...)" with (the equivalent of) a named pipe through which the output of the command "..." is sent (echo "$i" above).

The other point to note is the quotation marks around the array. It prevents word expansion of elements in the array.

An even better method is to avoid the exta call altogether:

examples=("apple|one|core" "trash|2|dummy")

for i in "${examples[@]}"; do
    IFS="|" read a b c <<< "$i"
    echo $a
    echo $c
done

.

Here the "<<<" operator pipes the value of the variable directly to STDIN.


My Arch Linux StuffForum EtiquetteCommunity Ethos - Arch is not for everyone

Offline

#3 2013-01-30 19:40:25

chris_l
Member
Registered: 2010-12-01
Posts: 390

Re: [solved]Why does this not work in bash?

I'm surprissed the script actually does works on zsh!
if you add to your script, after the "echo $c", a line like:

IFS="|" echo $IFS

And execute the script (from zsh - but bash does the same), it does not print a pipe, but a newline instead (the real IFS value)

You know why is that Xyne?

Last edited by chris_l (2013-01-30 19:43:32)


"open source is about choice"
No.
Open source is about opening the source code complying with this conditions, period. The ability to choose among several packages is just a nice side effect.

Offline

#4 2013-01-30 19:49:48

Xyne
Administrator/PM
Registered: 2008-08-03
Posts: 6,963
Website

Re: [solved]Why does this not work in bash?

chris_l wrote:

You know why is that Xyne?

Environment variables set on the line before the command are only set for the environment of the invoked command. In your example, $IFS is expanded in the current environment before being passed to "echo" as an argument.

Compare with this:

$ IFS="|" env | grep IFS
IFS=|

In this case, env checks the value of IFS in its local environment and finds the expected value. For the invocation of "read" above, the same thing happens. When read splits its input, it looks up IFS in its own environment.

Last edited by Xyne (2013-01-30 19:51:43)


My Arch Linux StuffForum EtiquetteCommunity Ethos - Arch is not for everyone

Offline

#5 2013-01-30 19:55:19

chris_l
Member
Registered: 2010-12-01
Posts: 390

Re: [solved]Why does this not work in bash?

Ahhh thank you


"open source is about choice"
No.
Open source is about opening the source code complying with this conditions, period. The ability to choose among several packages is just a nice side effect.

Offline

#6 2013-01-30 22:47:26

bslackr
Member
Registered: 2012-01-27
Posts: 131

Re: [solved]Why does this not work in bash?

Xyne wrote:

I think it is due to unexpected behavior in variable scoping when using pipes. I've run into this before but I don't remember the exact explanation.

..

Explanation:
Bash replaces "<(...)" with (the equivalent of) a named pipe through which the output of the command "..." is sent (echo "$i" above).

Awesome, thanks a lot for the explanation.

Last edited by bslackr (2013-01-30 22:47:44)

Offline

Board footer

Powered by FluxBB