6

I ran into a Zsh script and wanted to know its meaning. In the scripts below there is ${$(@f)$(egrep "$2","$file")} expression. From what I searched, @ is to use represent all positional parameters, but then the postfix letter f is not mentioned anywhere in above script and I couldn't find any online material saying its special meaning.

#!/usr/bin/env zsh

tmp_file="/tmp/_unsorted_find_n_grep"
echo "--- find <$2> in <$1>" > $tmp_file

find . -follow -iname "$1" | while read file
do
    timestamp=$(ls -l --time-style=+"%Y-%m-%d %H:%M:%S" "$file" | gawk '{print $6, $7}')
    linestack=${(@f)$(egrep "$2" "$file")}
    for line in $linestack 
do
    echo "$timestamp $file: $line" >> $tmp_file
done
done

cat $tmp_file | sort
rm $tmp_file
4

The character inside the parentheses are parameter expansion flags. They can be used around a variable substitution or a command substitution, e.g.

${(@f)SOME_VARIABLE}
${(@f)${(xyz)SOME_VARIABLE}}
${(@f)$(some-command)}

The flag f splits the result of the expansion at newlines. The flag @ ensures that the resulting array is expanded as multiple words; oddly, it only has effect inside double quotes, where it acts as a generalization of "$@".

Usually, these flags are used like this:

lines=("${(@f)$(egrep "$2" "$file")}")

This way lines becomes an array where each element is one line of output from egrep.

Here, the expansion is not in a context that allows multiple words, so the @ flag has no effect. The effect of the f flag is somewhat unintuitive: it forces an array context, so the IFS-separated words in the egrep output are stored in a temporary array which is then joined with the first character of IFS.

linestack=${(@f)$(egrep "$2" "$file")}

Contrast:

$ zsh -c 'a=${(f)$(echo hello world; echo wibble)}; print -lr $#a $a'
18
hello world wibble
$ zsh -c 'IFS=:$IFS; a=${(f)$(echo hello world; echo wibble)}; print -lr $#a $a'
18
hello:world:wibble
$ zsh -c 'IFS=:; a=${(f)$(echo hello world; echo wibble)}; print -lr $#a $a'
18
hello world
wibble

$ zsh -c 'a=(${(f)$(echo hello world; echo wibble)}); print -lr $#a $a' 
1
hello world wibble
$ zsh -c 'a=("${(@f)$(echo hello world; echo wibble)}"); print -lr $#a $a'
2
hello world
wibble
0

According to this zsh documentation

@    In double quotes, array elements are put into separate words. E.g., ‘"${(@)foo}"’
     is equivalent to ‘"${foo[@]}"’ and ‘"${(@)foo[1,2]}"’ is the same as ‘"$foo[1]"
     "$foo[2]"’. This is distinct from field splitting by the f, s or z flags, which still
     applies within each array element.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.