Is there a way to find the length of the array *
(files names) in zsh without using a for loop to increment some variable?
I naively tried echo ${#*[@]}
but it didn't work. (bash syntax are welcome as well)
${#*[@]}
would be the length of the $*
array also known as $@
or $argv
, which is the array of positional parameters (in the case of a script or function, that's the arguments the script or function received). Though you'd rather use $#
for that.
*
alone is just a glob pattern. In list context, that's expanded to the list of files in the current directory that match that pattern. As *
is a pattern that matches any string, it would expand to all file names in the current directory (except for the hidden ones).
Now you need to find a list context for that *
to be expanded, and then somehow count the number of resulting arguments. One way could be to use an anonymous function:
() {echo There are $# non hidden files in the current directory} *(N)
Instead of *
, I used *(N)
which is *
but with the N
(for nullglob) globbing qualifier which makes it so that if the *
pattern doesn't match any file, instead of reporting an error, it expands to nothing at all.
The expansion of *(N)
is then passed to that anonymous function. Within that anonymous function, that list of file is available in the $@
/$argv
array, and we get the length of that array with $#
(same as $#argv
, $#@
, $#*
or even the awkward ksh syntax like ${#argv[@]}
).
$*
an array at all? A "string" would be a better word for it, IMHO. Maybe it's different in zsh
?$*
is used inside double quotes, the words it's made of ($1
, $2
, ...) will be joined by the first char of IFS
without its elements being split on IFS
or spaces before that: (set -- 'a/b' 'a b'; IFS=:/; echo "<$*>")
(this latter digression is to illustrate that $*
is not somehow a string split an then rejoined in this particular syntax).$*
and $@
unquoted, but "$*"
is at least clearly a string whereas "$@"
is not a single string. Calling $*
an array and saying another name for it is $@
seems to confuse the purpose of these two variables.zsh
and yash
have real arrays and arrays whose indice start at 1, so they can represent that array as a normal array variable. That array goes by the *
, @
and argv
names in zsh
. Except that $@
is special inside double quotes on list contexts like it was in the Bourne shell. $*
inside double quotes expands to the concatenation of the elements like $argv
or $argv[*]
(or even $*[*]
) do, but that doesn't make it less that $argv
is an array variable, not a scalar variable.CommentedFeb 17, 2019 at 18:02files=(*) printf 'There are %d files\n' "${#files[@]}"
or
set -- * printf 'There are %d files\n' "$#"
You have to name the array first (as I did above with files
) or use the built-in array $@
by populating it with the wildcard, as I did in the second example. In the former, the "length" (number of files) of the array is done with the ${#arrayname[@]}
syntax. The number of elements in the built-in array is in $#
.
*
acts as a regex expression? I thought that it was a special array... although surprise me that there is no such array...CommentedFeb 9, 2019 at 1:59*[0] or *[1]
gives me some file name... now I'm puzzled.CommentedFeb 9, 2019 at 2:02*[0]
in zsh would expand to filenames that start with anything (or nothing) followed by any of the following characters: 0
. The square brackets designates a set of characters in that position. You likely have files that end with a zero in your current directory. To tell zsh to give you back the first element of a *
wildcard, you'd use parenthesis for array syntax: print -l *([1])
-- noting that zsh arrays start at 1 unless you set $KSH_ARRAYS
CommentedFeb 9, 2019 at 17:14
*
suggest you want to use shell globing mechanism here)?*
is not an array in the way you are using it, it is a shell glob. Arrays have nothing to do with your question unless you create an array as Jeff did in his answer. Your question is "How do I find how many files are in the current directory"echo *[0]
in zsh prints the 1st file name...