Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: triviality regarding $# counts



Hm.

typeset -ga aaa=("

abc


    def ghi

    jkl mno


    pqr

")

This makes aaa an array with a grand total of 1 element. Then you do this:

redline "linecount of aaa is: $#aaa ... Single element!"


Your prefix text is wrong; calling  $# a "linecount" is a gross mischaracterization. It never ever ever counts lines.  Never. It counts characters on a scalar variable, elements of an array.  Lines have nothing to do with it.

There are various transformations you can do that will map between lines on the one hand and array elements on the other, but that's always some command or special expansion flag doing an explicit transformation of the data - which is to say, changing it. Inside an array there is no concept of "lines" anywhere. By the same token, a piece of text containing newlines is not inherently any sort of array at all. (Well, zsh lets you treat a non-array variable as an array of individual characters, so in that sense it is an array, but each character is its own element in that case, and newlines are not in any way special.)

After your above declaration, the variable aaa is an array. The parentheses in the assignment would make it one even if you hadn't done typeset -a. Unless you un-array-ify the varaible, $#aaa will always be the number of elements in the array. It doesn't matter whether any of those elements contain newlines or not.  Your initialization has exactly one element, so $#aaa is 1. End of story.

You display things with print -l $array a lot; that's one of those transformations I mentioned above. When you do that to an array (and printf '%\n' "${array[@]}" does much the same thing in a POSIX-standard way), you will get output that starts each element of the array on a separate line. But that's all it does. Any newlines inside any of the array elements still show up as newlines in the output. So there is no guarantee that e.g. print -l $ary | wc -l  will produce the same number as $#ary.  In your aaa example, print -l $aaa gets you 8 lines. But it's still a single-element array. There is no $aaa[put anything in here] _expression_ that will isolate any of the lines of text; your only option is the whole thing, because it's all one element.  (You can use the "a string is an array of characters" trick to get substrings of that one element, e.g. ${aaa[1][2,4]} is "abc".  But again, the inner array is of characters, not lines.)

Then you do this, using another transformation: the f expansion flag:

ccc=( ${(@f)aaa} )


By not putting double quotes around the expansion of aaa, you are throwing away all the blank lines in its expansion. Since this is followed by a version using the quotes, I assume that's intentional. But as far as I can tell, the lack of quotation marks also removes any point for including the @ flag, since that flag's job is to maintain the identity of the separate elements of an array when the expansion is quoted.  If it does anything at all when not quoted, I'm unaware of it, but I admit that I could just be ignorant of some subtlety.

ddd=( "${(@f)aaa}" )


Now you're keeping the blank lines, so the (f) gives you a separate array element for every line that you get from the expansion - 8 of them, the same as the output of print -l $aaa | wc -l.
          

redline '\nddd=( "${(@f)aaa}" ) ... it seems like a lot of trouble to copy the array as it is.'


That comment confuses me; you aren't copying the array as it is at all! You've gone from aaa, which has one element, to ddd having 8 elements. That's a far cry from "copying as it is".

Your next comment says "8 not 6". I assume the expected 6 would be the middle lines, without the leading and trailing blank ones, but I know of no expansion that will produce that. You can get 6 elements out of aaa, like this:

eee=( $=aaa )

But it gets there by throwing away all space, including newlines.  So the six elements will be simply "abc", "def", "ghi", "jkl", "mno", and "pqr"


On Sat, Apr 13, 2024 at 11:14 AM Ray Andrews <rayandrews@xxxxxxxxxxx> wrote:
I dunno.  I've lost the example that I saved. Never mind, I'll keep an eye on it, and figure out next time. Red herring for now.  It's a bit confusing looking into typeset -p output. All this 'splitting' stuff is not simple. For now everything works and I'm quite sure it's a bit more orthodox.

   
A script:
---------------------------------------------------------------------------------

redline () { echo -e "$red$@$nrm" }

typeset -ga aaa=("
abc

    def ghi
    jkl mno

    pqr
")

redline "\naaa:"
print -l $aaa
redline "\naaa[1]:"
print -l $aaa[1]
redline "linecount of aaa is: $#aaa ... Single element!"

ccc=( ${(@f)aaa} )

redline '\nccc=( ${(@f)aaa} )'
print -l $ccc
redline "linecount of ccc is: $#ccc ... split, but blank lines gone."

ddd=( "${(@f)aaa}" )

redline '\nddd=( "${(@f)aaa}" ) ... it seems like a lot of trouble to copy the array as it is.'
print -l "$ddd[@]"  #... don't forget the quotes, idiot!"
redline "linecount of ddd is: $#ddd ... split, but blanks preserved and counted. NB EIGHT NOT SIX!"

redline "\nddd[1]: That's right it's blank, just as it should be."
print -l $ddd[1]
redline "\nddd[2]:"
print -l $ddd[2]

redline "And now the typesets\n"
typeset -p aaa
typeset -p ccc
typeset -p ddd

# Verbatim:

# typeset -a aaa=( $'\nabc\n\n\tdef ghi\n\tjkl mno\n\n\tpqr\n' )
# typeset -a ccc=( abc $'\tdef ghi' $'\tjkl mno' $'\tpqr' )
# typeset -a ddd=( '' abc '' $'\tdef ghi' $'\tjkl mno' '' $'\tpqr' '' )

# Aligned: Note the steeenking dollars ;-) ... and how they seem to replace the newlines. No?

# typeset -a aaa=( $'\nabc  \n\n\tdef ghi  \n\tjkl mno   \n\n\tpqr\n' )     #Original with blanks
# typeset -a ccc=(     abc    $'\tdef ghi' $'\tjkl mno   ' $'\tpqr' )           #No blanks.
# typeset -a ddd=(  '' abc '' $'\tdef ghi' $'\tjkl mno' '' $'\tpqr' '' )    #Blanks retained BUT not the same structure!

-----------------------------------------------------------------------------------

... so what about the dollars?

Anyway, it comes clear: '${(@f)....}' doesn't ADD (or retain) newlines it removes them!  (Tho splitting the array where they used to be.) Doesn't need them cuz elements aren't separated by newlines (like I thought) but by ... however it's done.  Single quotes indicate elements.  And, best of all, copying an array, blanks and all, (not shown above) turns out to be simple:

eee=( $aaa ) and:

eee=( "{$(@f)aaa}" )

... turns out to be a false equivalence where newlines are REMOVED.  Don't be fooled by 'printf' -- it might not show you the genuine state of the array.  Scalars show their blank lines automatically but we need:

print -l -- "$eee[@]"

... with arrays.  Trust typedef -p.




--
Mark J. Reed <markjreed@xxxxxxxxx>


Messages sorted by: Reverse Date, Date, Thread, Author