我需要在 bash 中将 CSV 转换为 TSV。我发现这解决方案,它运行良好,但不适用于所有数据集,如下所示。
例如,对于a.txt
:
a,"test, part2 ""the start""",b
sed
格式错误:
[ nir ]$ cat a.txt | sed -E 's/("([^"]*)")?,/\2\t/g'
a "test Op. 15 ""the start" b
#^ tab....^ tab..................^ tab
这里的问题:缺少,
、额外的选项卡、额外的引号。
只是为了引用即使 python 代码格式也很糟糕:
[ nir ]$ cat a.txt | csv2tsv.py
a "test, part2 ""the start""" b
#^ tab..........................^ tab
这里的问题:额外的引号。
csv2tsv.py
是:
csv.writer(sys.stdout, dialect='excel-tab').writerows(csv.reader(sys.stdin))
真正的转换应该是这样的:
a test, part2 "the start" b
#^ tab......................^ tab
希望得到一些反馈如何解决这个问题bash
。我在互联网上浏览了许多解决方案,但没有设法处理引号内引号上的那些引号:)
答案1
和mlr
:
mlr -N --icsv --otsvlite cat < file.csv > file.tsv
或者:
mlr -N --c2t --quote-none cat < file.csv > file.tsv
但请注意,如果 csv 字段包含制表符,则它最终不会在输出中转义,因此会引入额外的字段。
使用 GNU sed
,您可以执行相同的操作:
sed -E '
# append next line as long as there is not an even number
# of "s, to handle fields with newline. You can omit this line
# if the fields are guaranteed not to contain newlines:
:1; /^([^"]*"[^"]*")*[^"]*$/! {N;b1}
s/$/,/
s/(([^,"]*)|"((""|[^"])*)"),/\2\3\t/g
s/\t$//
s/""/"/g' < file.csv > file.tsv
假设输入是当前区域设置中的有效文本。首先sed
禁用LC_ALL=C sed...
本地化并将输入视为二进制输入以避免解码问题(如果担心速度,可能会加快速度)
答案2
bash 5.1 带有可加载的 CSV 模块
BASH_LOADABLES_PATH=${BASH/\/bin\//\/lib\/}
enable -f csv csv
csv -a fields "$line"
new_line=$(IFS=$'\t'; echo "${fields[*]}")
declare -p line fields new_line
输出
declare -- line="a,\"test, part2 \"\"the start\"\"\",b"
declare -a fields=([0]="a" [1]="test, part2 \"the start\"" [2]="b")
declare -- new_line="a test, part2 \"the start\" b"
#.....................^ tab......................^ tab
如果有一个包含选项卡的字段,则这是无效的。
在管道中:
IFS=$'\t'
cat file |
while IFS= read -r line; do
csv -a fields "$line"
echo "${fields[*]}"
done |
tail
虽然这是更惯用的 bash
IFS=$'\t'
while IFS= read -r line; do
csv -a fields "$line"
echo "${fields[*]}"
done < file | tail