74

我想在命令行上从查询输出到 MySQL 获取无标题 CSV 数据。我在与 MySQL 服务器不同的机器上运行此查询,因此所有那些带有“INTO OUTFILE”的 Google 答案都不好。

所以我跑mysql -e "select people, places from things"。输出看起来有点像这样的东西:

+--------+-------------+
| people | places      |
+--------+-------------+
|   Bill | Raleigh, NC |
+--------+-------------+

嗯,那不好。但是,嘿,看!如果我只是将它传递给任何东西,它会将它变成一个制表符分隔的列表:

people  places
Bill    Raleigh, NC

这更好——至少它是可程序解析的。但我不想要 TSV,我想要 CSV,也不想要那个标题。我可以用 摆脱标题mysql <stuff> | tail -n +2,但如果 MySQL 只是有一个标志来省略它,我想避免这样的麻烦。而且我不能只用逗号替换所有选项卡,因为它不能处理带有逗号的内容。

那么,如何让 MySQL 省略标头并以 CSV 格式提供数据?

4

9 回答 9

105

作为部分答案:mysql -N -B -e "select people, places from things"

-N告诉它不要打印列标题。-B是“批处理模式”,并使用制表符分隔字段。

如果制表符分隔值不够用,请参阅此 Stackoverflow Q&A

于 2013-05-16T16:33:22.703 回答
18

上述解决方案仅适用于特殊情况。在一般情况下,嵌入逗号、嵌入引号以及其他使 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 要求的内容。

于 2016-11-21T19:36:45.513 回答
14

我最终编写了自己的命令行工具来解决这个问题。它类似于cut,除了它知道如何处理带引号的字段等。这个工具与@Jimothy 的答案配对,允许我从远程 MySQL 服务器获取无头 CSV,我没有文件系统访问权限到我的本地机器上:

$ mysql -N -e "select people, places from things" | csvm -i '\t' -o ','
Bill,"Raleigh, NC"

github上的csvmaster

于 2013-07-28T16:18:39.443 回答
10

它是如何在客户端将结果保存到 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"} }}'

需要做什么的逻辑解释

  1. 首先,让我们看看数据在 RAW 模式下的样子(带--raw选项)。t数据库和表分别是dump3

    您可以看到从“新行”(第一行)开始的字段被分成三行,因为值中放置了新行。

mysql --skip-column-names --batch --raw -e 'select * from dump3' t

一行 2 新行
引号 " 反斜杠 \ 两个引号 "" 两个反斜杠 \\ 两个制表符换行
场的尽头

另一行 1 另一行描述,没有任何特殊字符
  1. 批处理模式下的输出数据(无选项) - 每条记录通过转义字符(如和--raw)更改为单行文本\ <tab>new-lines
mysql --skip-column-names --batch -e 'select * from dump3' t

一行 2 新行\n引号 " 反斜杠 \\ 两个引号 "" 两个反斜杠 \\\\ 两个制表符\t\t新行\n字段结尾
另一行 1 另一行描述,没有任何特殊字符
  1. 以CSV格式输出数据

线索是使用转义字符以 CSV 格式保存数据。

这样做的方法是将mysql --batch产生(\t作为\\反斜杠和\n换行符的制表符)的特殊实体转换为每个值(字段)的等效字节。然后整个值被 转义"和包围"。顺便说一句 - 使用相同的字符进行转义和封闭可以轻柔地简化输出和处理,因为您没有两个特殊字符。出于这个原因,您对值所做的一切(从 csv 格式的角度来看)就是更改"""whithin 值。以更常见的方式(分别转义和封闭\and ")您必须先更改\\\,然后更改"\".

以及命令的逐步解释

# 我们产生单行输出,如步骤 2 所示。
mysql --skip-column-names --batch -e 'select * from dump3' t

# 将字段分隔符设置为,因为 mysql 以这种方式生成
| awk -F'\t'

# 这开始从 mysql 数据中迭代每一行/记录 - awk 的标准行为
'{

# 字段分隔符为空,因为我们没有在第一个输出字段之前打印分隔符
sep="";

-- 遍历每个字段并将字段转换为 csv 正确值
for(i = 1; i <= NF; i++) {
-- 注意:\\ 下面的两个斜杠表示 awk 的 \ 因为它们被转义了

-- 把 \t 改成 <tab> 对应的字节
    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"}
    }
}'
于 2016-10-13T12:15:43.613 回答
4

使用 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,,
于 2015-07-01T06:07:16.610 回答
2

mysqldump实用程序可以帮助您,基本上可以--tab选择它是一个包装的 forSELECT 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

于 2013-03-26T22:46:00.897 回答
1

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 以表格格式输出。

于 2020-12-25T15:11:05.870 回答
1

您可以使用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的作者

于 2022-01-04T08:03:30.763 回答
1

如果您使用的是 mysql 客户端,则可以为每个会话设置 resultFormat,例如

mysql -h localhost -u root --resutl-format=json

或者

mysql -h localhost -u root --vertical

在此处查看完整的参数列表。

于 2020-09-01T12:32:57.687 回答