Here's another way to do it that's very similar to the answer by Wildcard:
files=( file1.csv file2.csv)
eval paste "<( cut -d, -f1 ${^files[@]} )"
Instead of a for
loop, this uses the ${^ ... }
expansion which is Zsh-specific.
The reason files
must be assigned first is that globbing is always done last, so if files
needs to be generated automatically (as in files=( *.csv )
) then something like ${^:-( *.csv )}
would expand only after all the other expansions have occurred. We want it to expand first.
The ${^ ... }
expansion causes the resulting array to act like the result of brace expansion. For example, assign x=(a b)
and then compare echo ${x}y
to echo ${^x}y
.
The quoting is necessary to trick Zsh into treating the surrounding text like a literal string. Otherwise, it would split the command line at the spaces, so our ${^ ... }
expansion would reduce to ""${^ ... }""
; that is, each element would be surrounded only by an empty string. That is,
echo "<( cut -d, -f1 ${^files[@]} )"
and
echo "<( cut -d, -f1 "\
${^files[@]}\
" )"
are equivalent, but are not the same as
echo <( cut -d, -f1 ${^files[@]} )
But quoting introduces a new problem: the command line is parsed and split without regard to the expansion taking place. That is, even though we have effectively entered
paste <( cut -d, -f1 file1.csv ) <( cut -d, -f1 file2.csv )
as desired, this is in fact parsed as
paste '<( cut -d, -f1 file1.csv )' '<( cut -d, -f1 file2.csv )'
Therefore we need eval
to re-parse the correctly formed expression. To see this in action, compare
setopt noxtrace
eval paste "<( cut -d, -f1 ${^files[@]} )" 1>/dev/null 2>&1
to
setopt xtrace
eval paste "<( cut -d, -f1 ${^files[@]} )" 1>/dev/null 2>&1
I hoped that some combination of nested expansions, the ${ ... :- ... }
expansion, and the parameter expansion flags Q
, z
, and/or s
would lead to re-evaluation without eval
, but evidently that's not the case. I also wish there was a way to force globbing, but again that seems impossible.
cat file*.csv | cut -d, -f1 | paste
do what you try to archive?