我经常做这样的操作
paste <(cut -d, -f1 file1.csv) <(cut -d, -f1 file2.csv)
对于多个文件来说,这是非常乏味的。
我可以自动化这个过程,例如通过通配符吗?我可以保存cut
结果
typeset -A cut_results
for f in file*.csv; do
cut_results[$f]="$(cut -d, -f1 $f)"
done
但我不知道如何从那里开始。
答案1
您可以通过通配符自动执行此操作,特别是e
全局限定符,加上eval
,但它并不漂亮,而且引用也很棘手:
eval paste *.csv(e\''REPLY="<(cut -d, -f1 $REPLY)"'\')
- 之间的部分
\'…\'
是为全局的每个匹配执行的一些代码。它是在变量REPLY
设置为匹配的情况下执行的,并且可以修改它。 - 我将代码放在单引号中,以便在解析 glob 时它不会扩展。
- 如果匹配为 ,则代码
REPLY="<(cut -d, -f1 $REPLY)"
生成字符串。双引号是必需的,这样除了替换 的值之外,执行代码时等号后面的部分不会展开。<(cut -d, -f1 file1.csv)
file1.csv
e
REPLY
- 由于每个通配文件都被一个字符串替换,
将复杂性隐藏在函数中会更好。经过最低限度的测试。
function map {
emulate -LR zsh
local cmd pre
cmd=()
while [[ $# -ne 0 && $1 != "--" ]]; do
cmd+=($1)
shift
done
if ((!$#)); then
echo >&2 "Usage: $0: COMMAND [ARGS...] -- PREPROCESSOR [ARGS...] -- FILES..."
return 125
fi
shift
while [[ $# -ne 0 && $1 != "--" ]]; do
pre+="${(q)1} "
shift
done
if ((!$#)); then
echo >&2 "Usage: $0: COMMAND [ARGS...] -- PREPROCESSOR [ARGS...] -- FILES..."
return 125
fi
shift
eval "${(@q)cmd}" "<($pre${(@q)^@})"
}
示例用法(语法让人想起zargs
):
map paste -- cut -d, -f1 -- *.csv
答案2
我认为你的第一行对于一个简单的单行来说已经是最好的了。
如果有一堆具有不同名称的文件,您可以使用简单的历史扩展“作弊”来减少重复输入:
第一次运行<(cut -d, -f1
注意尾随空格。另请注意,此命令将为您提供辅助提示;只需按Ctrl- C。唯一的一点是将其添加到历史记录中。
下次运行paste !!file1.csv) !!file2.csv)
将!!
扩展到上一个命令运行的完整内容,包括尾随空格。请注意,如果您忘记了尾随的右括号,您将收到辅助提示;如果发生这种情况,您可以输入Ctrl-C并重试。
这有点老套,但对于一次性使用来说已经足够了。如果您经常这样做,您可能会编写一个 bash 函数。
答案3
尝试awk
awk '{L[FNR]=L[FNR] $1 "\t"}END{for(i=1;i<=FNR;i++)print L[i]}' *.csv
或者粘贴和sed
paste *.csv | sed 's/ [^\t]*//g'
答案4
假设你的论点在"$@"
,我相信类似:
eval "paste $(printf "<( cut -d, -f1 %q ) " "$@")"
应该这样做。