我会看看@hayden-schiff 建议的 csvkit
如果你不想走那条路,这就是我想出的。
尽管有一些警告:
更新:您的编辑表明您的输入文件并不总是有两列,我还没有找到一种方法让 cut 返回一个空字符串(但带有换行符)旧版本的工作。
所以现在我将逐行浏览 CSV,将值(或空字符串)抓取到每个输入文件的临时文件中,然后将它们全部粘贴到最后:
#!/usr/bin/env bash
FILES="infile_??.csv"
FINAL="final_table.csv"
COLUMN="3"
# Delete ${FINAL} if it exists.
[[ -f "${FINAL}" ]] && rm ${FINAL}
TMPFILES=""
for f in ${FILES}; do
while IFS='' read -r line || [[ -n "$line" ]]; do
val=$(cut -f ${COLUMN} -d, -s <<< $line)
[[ -n "${val}" ]] && echo "${val}" || echo " "
done < "${f}" > "${f}.${COLUMN}.csv"
TMPFILES="${TMPFILES} ${f}.${COLUMN}.csv"
done
paste -d, ${TMPFILES} > ${FINAL}
rm ${TMPFILES}
以下是我的原始版本,它假设所有文件确实至少有你想要阅读的列数:
- 对我的解决方案不满意,因为您一遍又一遍地打开同一个文件以同时进行读取和写入。我很想知道一种将未知数量的进程的输出作为不同的输入流传递到单个最终进程的方法。
- 在您的描述中,您需要第二列,但您使用
--complement 选项返回除第二列之外的所有内容。这让我有点失望。我忽略了这一点并按照您的描述进行。
原来是这样:
#!/usr/bin/env bash
FILES="infile_??.csv"
FINAL="final_table.csv"
COLUMN="2"
# Delete ${FINAL} if it exists.
[[ -f "${FINAL}" ]] && rm ${FINAL}
for f in $FILES; do
if [[ -f ${FINAL} ]]; then
# ${FINAL} already exists from an earlier iteration
# If you have "moreutils" installed, you can use sponge:
# cut -d',' -f 2 ${f} | paste -d',' ${FINAL} - | sponge ${FINAL}
# otherwise you can use "echo" in the way below:
echo "$(cut -d',' -f ${COLUMN} ${f} | paste -d',' ${FINAL} -)" > ${FINAL}
else
# ${FINAL} does not yet exist, we have to create it.
cut -d',' -f ${COLUMN} ${f} > ${FINAL}
fi
done
更新:我的理解是它应该取每个输入文件的第二列,并将它们逐列写入输出文件,如下所示:
我的输入文件:
infile_01.txt:
111, 112, 113
121, 122, 123
131, 132, 133
141, 142, 143
infile_02.txt:
211, 212, 213
221, 222, 223
231, 232, 233
241, 242, 243
(所以每个数字是100 *(文件号)+ 10 *(行号)+(列号)。)
我的代码在我的计算机上产生以下输出:
112, 212
122, 222
132, 232
142, 242
如果这是您得到的,但不是您想要的,请告诉我输出文件与这些输入文件的外观。如果这不是你得到的,请用我的文件在你的系统上运行它并告诉我你得到了什么。