spi*_*ech 57 mysql csv command-line
我想在命令行上从查询输出中获取无头CSV数据.我在与MySQL服务器不同的机器上运行此查询,因此所有使用"INTO OUTFILE"的Google答案都不好.
所以我跑了mysql -e "select people, places from things".那输出看起来像这样的东西:
+--------+-------------+
| people | places |
+--------+-------------+
| Bill | Raleigh, NC |
+--------+-------------+
Run Code Online (Sandbox Code Playgroud)
嗯,这不好.但是,嘿,看!如果我只是把它管道到任何东西,它会把它变成一个以制表符分隔的列表:
people places
Bill Raleigh, NC
Run Code Online (Sandbox Code Playgroud)
那更好 - 至少它是以编程方式解析的.但我不想要TSV,我想要CSV,而且我不想要那个标题.我可以摆脱标题mysql <stuff> | tail -n +2,但这是一个麻烦我想避免如果MySQL只有一个标志来省略它.而且我不能用逗号替换所有选项卡,因为它不处理带有逗号的内容.
那么,我怎样才能让MySQL省略标题并以CSV格式提供数据?
Jim*_*thy 85
作为部分答案: mysql -N -B -e "select people, places from things"
-N告诉它不要打印列标题.-B是"批处理模式",并使用选项卡分隔字段.
如果制表符分隔值不够,请参阅此Stackoverflow问答.
spi*_*ech 13
我最后编写了自己的命令行工具来处理这个问题.它类似于cut,除了它知道如何处理引用字段等.这个工具,与@Jimothy的答案配对,允许我从远程MySQL服务器获取无头CSV我没有文件系统访问到我的本地机器上使用此命令:
$ mysql -N -e "select people, places from things" | csvm -i '\t' -o ','
Bill,"Raleigh, NC"
Run Code Online (Sandbox Code Playgroud)
Chr*_*son 10
以上解决方案仅适用于特殊情况.嵌入式逗号,嵌入式引号,以及在一般情况下使CSV变硬的其他因素,您将遇到各种各样的麻烦.
帮自己一个忙,并使用一般解决方案 - 做得对,你再也不用考虑了.一个非常强大的解决方案是csvkit命令行实用程序 - 通过Python可用于所有操作系统.通过安装pip install csvkit.这将为您提供正确的CSV数据:
mysql -e "select people, places from things" | csvcut -t
Run Code Online (Sandbox Code Playgroud)
这会产生以逗号分隔的数据,标题仍然存在.要删除标题行:
mysql -e "select people, places from things" | csvcut -t | tail -n +2
Run Code Online (Sandbox Code Playgroud)
这产生了OP要求的内容.
如何在没有其他非标准工具的情况下在客户端将结果保存为CSV.此示例仅使用 mysql客户端和awk.
一条线:
mysql --skip-column-names --batch -e 'select * from dump3' t | awk -F'\t' '{ sep=""; for(i = 1; i <= NF; i++) { gsub(/\\t/,"\t",$i); gsub(/\\n/,"\n",$i); gsub(/\\\\/,"\\",$i); gsub(/"/,"\"\"",$i); printf sep"\""$i"\""; sep=","; if(i==NF){printf"\n"}}}'
需要做什么的逻辑解释
首先,让我们看看RAW模式下的数据如何(带--raw选项).数据库和表分别是t和dump3
您可以看到从"新行"(在第一行)开始的字段由于在值中放置了新行而被拆分为三行.
mysql --skip-column-names --batch --raw -e 'select * from dump3' t one line 2 new line quotation marks " backslash \ two quotation marks "" two backslashes \\ two tabs new line the end of field another line 1 another line description without any special chars
--raw选项) - 通过转义字符\ <tab>和,将每个记录更改为单行文本new-linesmysql --skip-column-names --batch -e 'select * from dump3' t one line 2 new line\nquotation marks " backslash \\ two quotation marks "" two backslashes \\\\ two tabs\t\tnew line\nthe end of field another line 1 another line description without any special chars
线索是使用转义字符以CSV格式保存数据.
做到这一点的方法是将转换哪个特殊实体mysql --batch产生(\t如凸片\\作为backshlash和\n作为新行)成每个值(字段)等效字节.然后整个值被转义并被"封闭".顺便说一句 - 使用相同的字符进行转义和封闭可以轻松简化输出和处理,因为您没有两个特殊字符.因此,您需要处理的所有值(从csv格式的角度来看)都要更改"为""whithin值.在更普遍的方式(与逃逸,分别封闭\和"),您必须首先改变\对\\,然后改变"成\".
并且命令的解释一步一步:
# we produce one-line output as showed in step 2.
mysql --skip-column-names --batch -e 'select * from dump3' t
# set fields separator to because mysql produces in that way
| awk -F'\t'
# this start iterating every line/record from the mysql data - standard behaviour of awk
'{
# field separator is empty because we don't print a separator before the first output field
sep="";
-- iterating by every field and converting the field to csv proper value
for(i = 1; i <= NF; i++) {
-- note: \\ two shlashes below mean \ for awk because they're escaped
-- changing \t into byte corresponding to <tab>
gsub(/\\t/, "\t",$i);
-- changing \n into byte corresponding to new line
gsub(/\\n/, "\n",$i);
-- changing two \\ into one \
gsub(/\\\\/,"\\",$i);
-- changing value into CSV proper one literally - change " into ""
gsub(/"/, "\"\"",$i);
-- print output field enclosed by " and adding separator before
printf sep"\""$i"\"";
-- separator is set after first field is processed - because earlier we don't need it
sep=",";
-- adding new line after the last field processed - so this indicates csv record separator
if(i==NF) {printf"\n"}
}
}'