将 MySQL 命令行结果的输出格式更改为 CSV
Posted
技术标签:
【中文标题】将 MySQL 命令行结果的输出格式更改为 CSV【英文标题】:Change output format for MySQL command line results to CSV 【发布时间】:2013-03-16 10:31:12 【问题描述】:我想在命令行上从查询输出到 mysql 获取无标题 CSV 数据。我在与 MySQL 服务器不同的机器上运行此查询,因此所有那些带有“INTO OUTFILE”的 Google 答案都不好。
所以我运行mysql -e "select people, places from things"
。输出的东西看起来有点像这样:
+--------+-------------+
| people | places |
+--------+-------------+
| Bill | Raleigh, NC |
+--------+-------------+
好吧,那不好。但是,嘿,看!如果我只是将它传递给 anything,它会将它变成一个制表符分隔的列表:
people places
Bill Raleigh, NC
这样更好——至少它可以通过编程方式解析。但我不想要 TSV,我想要 CSV,也不想要那个标题。我可以用mysql <stuff> | tail -n +2
去掉标题,但如果MySQL 只是有一个标志可以省略它,我想避免这种麻烦。而且我不能只用逗号替换所有选项卡,因为它不能处理包含逗号的内容。
那么,我怎样才能让 MySQL 省略标头并以 CSV 格式提供数据?
【问题讨论】:
【参考方案1】:作为部分答案:mysql -N -B -e "select people, places from things"
-N
告诉它不要打印列标题。 -B
是“批处理模式”,使用制表符分隔字段。
如果制表符分隔值不够用,请参阅this *** Q&A。
【讨论】:
【参考方案2】:上述解决方案仅适用于特殊情况。在一般情况下,嵌入逗号、嵌入引号以及其他使 CSV 难以处理的事情会让您自己陷入各种麻烦。
帮自己一个忙,使用通用解决方案 - 做对了,您就不必再考虑它了。一个非常强大的解决方案是csvkit
命令行实用程序 - 通过 Python 可用于所有操作系统。通过pip install csvkit
安装。这将为您提供正确的 CSV 数据:
mysql -e "select people, places from things" | csvcut -t
这会生成逗号分隔的数据,而标题仍然存在。删除标题行:
mysql -e "select people, places from things" | csvcut -t | tail -n +2
这会产生 OP 请求的内容。
【讨论】:
正是我想要的!并且(基于***.com/questions/356578/…)您可以同时生成一个本地CSV文件,例如:mysql -e "select people, places from things" | csvcut -t > output.csv
【参考方案3】:
我最后写了my own command-line 工具来解决这个问题。它类似于cut
,除了它知道如何处理引用的字段等。这个工具与@Jimothy 的答案配对,允许我从我没有文件系统访问权限的远程 MySQL 服务器获取无标题的 CSV 到我的本地机器上用这个命令:
$ mysql -N -e "select people, places from things" | csvm -i '\t' -o ','
Bill,"Raleigh, NC"
csvmaster on github
【讨论】:
编写自定义实用程序并不能真正帮助那些不能(或不想)下载和构建它的人。我认为另一个答案更实用。 没错,内置解决方案通常更可取。但是,该问题专门要求分隔符是逗号,而不是制表符,并且没有内置的方法可以做到这一点。 Jimothy 的回答只涵盖了问题的一半。【参考方案4】:这是如何在客户端将结果保存到 CSV 而无需额外的非标准工具。
此示例仅使用mysql
客户端和awk
。
单行:
mysql --skip-column-names --batch -e 'select * from dump3' t | awk -F'\t'' sep=""; for(i = 1; i需要做什么的逻辑解释
首先,让我们看看数据在 RAW 模式下的样子(使用--raw
选项)。数据库和表分别是t
和dump3
您可以看到从“新行”(第一行)开始的字段被分成三行,因为值中放置了新行。
-
批量输出数据(不带
--raw
选项)-每条记录通过转义字符(如\
<tab>
和new-lines
)变为单行文本
-
和CSV格式的数据输出
提示是将数据保存为带有转义字符的 CSV 格式。
这样做的方法是将mysql --batch
生成的特殊实体(\t
作为制表符,\\
作为反斜杠,\n
作为换行符)转换为每个值(字段)的等效字节。
然后整个值被"
转义并被"
包围。
顺便说一句 - 使用相同的字符进行转义和封闭可以轻轻地简化输出和处理,因为您没有两个特殊字符。
出于这个原因,您对值所做的所有事情(从 csv 格式的角度来看)就是将 "
更改为 ""
whithin 值。以更常见的方式(分别转义和封闭\
和"
),您必须首先将\
更改为\\
,然后将"
更改为\"
。
以及命令的逐步解释:
# 我们产生单行输出,如步骤 2 所示。 mysql --skip-column-names --batch -e 'select * from dump3' t # 将字段分隔符设置为,因为 mysql 以这种方式生成 | awk -F'\t' # 这开始从 mysql 数据中迭代每一行/记录 - awk 的标准行为 ' # 字段分隔符为空,因为我们没有在第一个输出字段之前打印分隔符 sep=""; -- 遍历每个字段并将字段转换为 csv 正确值 for(i = 1; i 对应的字节 gsub(/\\t/, "\t",$i); -- 把 \n 改成新行对应的字节 gsub(/\\n/, "\n",$i); -- 将两个 \\ 变为一个 \ gsub(/\\\\/,"\\",$i); -- 将 value 更改为 CSV 正确的字面值 - 将 " 更改为 "" gsub(/"/, "\"\"",$i); -- 打印由 " 包围的输出字段并在之前添加分隔符 printf sep"\""$i"\""; -- 处理第一个字段后设置分隔符 - 因为之前我们不需要它 sep=","; -- 在处理完最后一个字段后添加新行 - 这表示 csv 记录分隔符 if(i==NF) printf"\n" '【讨论】:
"--raw" 不转义 \t、\n 和 "。因此,请注意此选项。如果任何列包含此字符,此选项可能会破坏您的结果。 没错,这就是解决方案没有“--raw”选项的原因。它仅在第一步中用于显示准确数据。 遇到了一种情况,即“用完这个”失败 - 看起来 URL 中的百分号是什么让它窒息?我正在使用的 mySQL 转储是公开的(2017 年 1 月的最新转储):ifarchive.org/indexes/if-archiveXinfoXifdb.html - 导致问题的查询:“SELECT * FROM games WHERE id='ju8qpl62enpiutnm';” -- awk 是否需要对添加的百分号进行转义? 我对 % 符号没有任何问题。您能否提供唯一的“mysql”命令的输出,不带 | awk ... ? 这应该被标记为正确答案。救了我的命,谢谢!如果有人遇到百分号问题,只需添加gsub(/%/, "%%", $i);
【参考方案5】:
使用 sed 怎么样?它是大多数(所有?)Linux 操作系统的标准配置。
sed 's/\t/<your_field_delimiter>/g'
。
此示例使用 GNU sed (Linux)。对于 POSIX sed (AIX/Solaris),我相信您会键入文字 TAB 而不是 \t
示例(用于 CSV 输出):
#mysql mysql -B -e "select * from user" | while read; do sed 's/\t/,/g'; done
localhost,root,,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,,,,,0,0,0,0,,
localhost,bill,*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,,,,,0,0,0,0,,
127.0.0.1,root,,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,,,,,0,0,0,0,,
::1,root,,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,,,,,0,0,0,0,,
%,jim,*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,,,,,0,0,0,0,,
【讨论】:
如果您的单元格值可以包含制表位,这可能需要小心。此外,如果您的单元格值包含逗号,则此方法将生成具有可变列数的 CSV,而无法识别什么是列分隔逗号以及内容的一部分。像sed
和 awk
这样的传统 *nix 工具不适合这种情况,因为正确转义内容会很快变得丑陋。
这是可以构建的。如果您的数据包含逗号,您可以使用不同的字段分隔符。绝对不是一个完整的、一刀切的解决方案【参考方案6】:
mysqldump
实用程序可以为您提供帮助,基本上使用--tab
选项,它是SELECT INTO OUTFILE
语句的包装。
例子:
mysqldump -u root -p --tab=/tmp world Country --fields-enclosed-by='"' --fields-terminated-by="," --lines-terminated-by="\n" --no-create-info
这将创建 csv 格式的文件/tmp/Country.txt
【讨论】:
根据 'mysqldump' 手册页,--tab
功能将文件放到 MySQL 服务器上,而不是您的客户端机器上。【参考方案7】:
如果您使用的是 mysql 客户端,您可以为每个会话设置 resultFormat,例如
mysql -h localhost -u root --resutl-format=json
或
mysql -h localhost -u root --vertical
查看完整的参数列表here。
【讨论】:
【参考方案8】:mysql客户端可以检测到输出fd,如果fd是S_IFIFO(pipe)则不输出ASCII TABLES,如果fd是字符设备(S_IFCHR)则输出ASCII TABLES。
您可以使用 --table 强制输出 ASCII TABLES,例如:
$mysql -t -N -h127.0.0.1 -e "select id from sbtest1 limit 1" | cat
+--------+
| 100024 |
+--------+
-t, --table 以表格形式输出。
【讨论】:
【参考方案9】:您可以使用spyql读取mysql的制表符分隔输出并生成逗号分隔的CSV并关闭标题写入:
$ mysql -e "SELECT 'Bill' AS people, 'Raleigh, NC' AS places" | spyql -Oheader=False "SELECT * FROM csv TO csv"
Bill,"Raleigh, NC"
spyql 检测输入是否有标题以及分隔符是什么。默认情况下,输出分隔符是逗号。如果您愿意,可以手动指定所有这些选项:
$ mysql -e "SELECT 'Bill' AS people, 'Raleigh, NC' AS places" | spyql -Idelimiter="'\t'" -Iheader=True -Odelimiter="," -Oheader=False "SELECT * FROM csv TO csv"
Bill,"Raleigh, NC"
我不会关闭 mysql 上的标头写入,因为 spyql 可以利用它,例如,如果您选择生成 JSON 而不是 CSV:
$ mysql -e "SELECT 'Bill' AS people, 'Raleigh, NC' AS places" | spyql "SELECT * FROM csv TO json"
"people": "Bill", "places": "Raleigh, NC"
或者如果您需要引用您的列:
$ mysql -e "SELECT 'Bill' AS people, 'Raleigh, NC' AS places" | spyql -Oindent=2 "SELECT *, 'I am and I live in .'.format(people, places) AS message FROM csv TO json"
"people": "Bill",
"places": "Raleigh, NC",
"message": "I am Bill and I live in Raleigh, NC."
免责声明:我是spyql的作者
【讨论】:
以上是关于将 MySQL 命令行结果的输出格式更改为 CSV的主要内容,如果未能解决你的问题,请参考以下文章