Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.debian.user > #176429 > unrolled thread

Bash different behaviour of read / strings in jessie versus stretch (regression?)

Started by"foo fighter" <lopiuh@gmx.net>
First post2017-01-07 22:00 +0100
Last post2017-01-10 14:40 +0100
Articles 4 — 2 participants

Back to article view | Back to linux.debian.user


Contents

  Bash different behaviour of read / strings in jessie versus stretch  (regression?) "foo fighter" <lopiuh@gmx.net> - 2017-01-07 22:00 +0100
    Re: Bash different behaviour of read / strings in jessie versus stretch (regression?) Greg Wooledge <wooledg@eeg.ccf.org> - 2017-01-09 15:00 +0100
      Aw: Re: Bash different behaviour of read / strings in jessie versus  stretch (regression?) "foo fighter" <lopiuh@gmx.net> - 2017-01-10 01:20 +0100
        Re: Re: Bash different behaviour of read / strings in jessie versus stretch (regression?) Greg Wooledge <wooledg@eeg.ccf.org> - 2017-01-10 14:40 +0100

#176429 — Bash different behaviour of read / strings in jessie versus stretch (regression?)

From"foo fighter" <lopiuh@gmx.net>
Date2017-01-07 22:00 +0100
SubjectBash different behaviour of read / strings in jessie versus stretch (regression?)
Message-ID<sX6fL-5fd-9@gated-at.bofh.it>
Hi,
 
the same code behaves different in jessie and stretch. Does someone know why, and if this could be a regression?
In jessie "version 1" and "version 2" behave identical. In stretch, "version 2" behaves different (only one item in array)
The code difference between "version 1" and "version 2" lies in quotation marks around  $_inputstring

Yours lopiuh
 
Code:
 
#!/usr/bin/env bash
set -o errexit  # (set -e)
set -o nounset  # (set -u)
set -o pipefail #
shopt -s nullglob # enable
#shopt -s failglob
#set -o noglob

clear
echo "===========================Version 1==========================="
echo "Bash Version: $BASH_VERSION"
echo -n "IFS:"; echo "$IFS" | cat -vte
echo

unset _array; declare -a _array

_inputstring="$(echo -e "item01\nitem02\nitem03")"
echo "inputstring:"
echo -n "$_inputstring" | xxd; echo

read -ra _array <<<$(echo $_inputstring) # <<< NO -"- around string
#read -ra _array <<<"$_inputstring" #<--- only one item, even in jessie
#read -ra _array <<<$_inputstring #<--- only one item, even in jessie

echo "itemcount of array: ${#_array[@]}"
for i in "${_array[@]}"; do
	echo -n "element: "; echo -n "$i" | xxd; echo
done
#
echo "===========================Version 2==========================="
echo "Bash Version: $BASH_VERSION"
echo -n "IFS:"; echo "$IFS" | cat -vte
echo

unset _array; declare -a _array

_inputstring="$(echo -e "item01\nitem02\nitem03")"
echo "inputstring:"
echo -n "$_inputstring" | xxd; echo

read -ra _array <<<$(echo "$_inputstring") # <<< WITH -"- around string
#read -ra _array <<<"$_inputstring" #<--- only one item, even in jessie
#read -ra _array <<<$_inputstring #<--- only one item, even in jessie

echo "itemcount of array: ${#_array[@]}"
for i in "${_array[@]}"; do
	echo -n "element: "; echo -n "$i" | xxd; echo
done

exit

Output of jessie:
===========================Version 1===========================
Bash Version: 4.3.30(1)-release
IFS: ^I$
$

inputstring:
0000000: 6974 656d 3031 0a69 7465 6d30 320a 6974  item01.item02.it
0000010: 656d 3033                                em03

itemcount of array: 3
element: 0000000: 6974 656d 3031                           item01

element: 0000000: 6974 656d 3032                           item02

element: 0000000: 6974 656d 3033                           item03

===========================Version 2===========================
Bash Version: 4.3.30(1)-release
IFS: ^I$
$

inputstring:
0000000: 6974 656d 3031 0a69 7465 6d30 320a 6974  item01.item02.it
0000010: 656d 3033                                em03

itemcount of array: 3
element: 0000000: 6974 656d 3031                           item01

element: 0000000: 6974 656d 3032                           item02

element: 0000000: 6974 656d 3033                           item03


Output of stretch:
===========================Version 1===========================
Bash Version: 4.4.5(1)-release
IFS: ^I$
$

inputstring:
00000000: 6974 656d 3031 0a69 7465 6d30 320a 6974  item01.item02.it
00000010: 656d 3033                                em03

itemcount of array: 3
element: 00000000: 6974 656d 3031                           item01

element: 00000000: 6974 656d 3032                           item02

element: 00000000: 6974 656d 3033                           item03

===========================Version 2===========================
Bash Version: 4.4.5(1)-release
IFS: ^I$
$

inputstring:
00000000: 6974 656d 3031 0a69 7465 6d30 320a 6974  item01.item02.it
00000010: 656d 3033                                em03

itemcount of array: 1
element: 00000000: 6974 656d 3031                           item01

[toc] | [next] | [standalone]


#176498 — Re: Bash different behaviour of read / strings in jessie versus stretch (regression?)

FromGreg Wooledge <wooledg@eeg.ccf.org>
Date2017-01-09 15:00 +0100
SubjectRe: Bash different behaviour of read / strings in jessie versus stretch (regression?)
Message-ID<sXIEq-4UO-15@gated-at.bofh.it>
In reply to#176429
On Sat, Jan 07, 2017 at 09:54:34PM +0100, foo fighter wrote:
> In jessie "version 1" and "version 2" behave identical. In stretch, "version 2" behaves different (only one item in array)

> echo "===========================Version 2==========================="
[...]
> _inputstring="$(echo -e "item01\nitem02\nitem03")"

_inputstring=$'item01\nitem02\nitem03'

> read -ra _array <<<$(echo "$_inputstring") # <<< WITH -"- around string

This is just wrong.  You're using $(echo) which is silly and pointless,
and you've failed to quote the result, and you've failed to tell read
not to stop reading at the first newline, and you've failed to set IFS
to use only newlines as delimiters between array elements.

The only way yours "works" at all is because two of your errors are
canceling each other.  The unquoted $(echo) is doing word splitting
and then reassembling the result, effectively replacing each newline
with a space, so that you only have a single line of input, which is
why read sees the entire input....  Yikes.

Really, you should be using mapfile (a.k.a. readarray) when reading
a multi-line input where each line is intended to become one array
element.

_inputstring=$'item01\nitem02\nitem03'
mapfile -t _array <<< "$_inputstring"
declare -p _array

Try that in both versions of bash, and I believe that it will work
consistently and correctly.

Now I get to start guessing.  My first guess is that you are doing
something like this:

tmp=$(some program that writes multiple lines)
read -ra array <<< $(echo "tmp")  # wrong

Just do this instead:

mapfile -t array < <(some program that writes multiple lines)

[toc] | [prev] | [next] | [standalone]


#176522 — Aw: Re: Bash different behaviour of read / strings in jessie versus stretch (regression?)

From"foo fighter" <lopiuh@gmx.net>
Date2017-01-10 01:20 +0100
SubjectAw: Re: Bash different behaviour of read / strings in jessie versus stretch (regression?)
Message-ID<sXSkp-2vm-1@gated-at.bofh.it>
In reply to#176498
Hi Greg,
 
thanks for your answer. I used "read" because it offered different delimiter (IFS) at the same time as far as my elements are seperateed by LF or (one) space (per element). You are right it is stdout of other programs. Furthermore globbing was not intended in this area but I did not want to toggle globbing off/on  with "set" within the script. As far as mapfile needs LF as delimiter (as far as I see) I do now a search and replace (space to LF)

 
mapfile -t _array <<<"${_inputstring// /$'\n'}"
 
This works in both bash versions.
 
Thaaaaanks !!
 
Yours
 
Lopiuh

[toc] | [prev] | [next] | [standalone]


#176550 — Re: Re: Bash different behaviour of read / strings in jessie versus stretch (regression?)

FromGreg Wooledge <wooledg@eeg.ccf.org>
Date2017-01-10 14:40 +0100
SubjectRe: Re: Bash different behaviour of read / strings in jessie versus stretch (regression?)
Message-ID<sY4OC-1Tf-37@gated-at.bofh.it>
In reply to#176522
On Tue, Jan 10, 2017 at 01:10:53AM +0100, foo fighter wrote:
> mapfile -t _array <<<"${_inputstring// /$'\n'}"

For larger inputs, this will probably be more efficient:

mapfile -t _array < <(your command | tr ' ' '\n')

Bash's substitution can be rather slow on large strings.

[toc] | [prev] | [standalone]


Back to top | Article view | linux.debian.user


csiph-web