【问题标题】:How to run a sed command to take in a sed file and output a sql file如何运行 sed 命令以获取 sed 文件并输出 sql 文件
【发布时间】:2015-05-09 21:09:51
【问题描述】:

更新问题:http://pastebin.com/SrVC8PvW:

Sed 文件 — csv2sql.sed

s|\([A-Z]*\),\([A-Z]*\),\([A-Za-z0-9’ ()\.\!-]*\),\([A-Za-z0-9’ ()\.\!-]*\),\([0-9]*\)|INSERT INTO `Schedule` (`ID`,`YEAR`,`NUMBER`,`TeamR1`,`TeamR2`, `TeamR3`, `TeamB1`, `TeamB2`, `TeamB3`) VALUES ('\1', '\2', '\3', '\4', '\5', '\6', '\7', '\8', '\9');|g

CSV 文件(部分):

1,2015,0,57,3310,3676,624,3802,3481

控制台错误:

sed: file csv2sql.sed line 1: invalid reference \9 on `s` command's RHS

我有一个名为 csv2sql.sed 的 SED 文件,它将接收一个 CSV 文件并将其输出为 SQL 语法。我想运行脚本,并在同一文件夹中有一个名为 schedules.sql 的 new sql 文件。

如何解决我遇到的错误?

【问题讨论】:

  • man sed。你会比你输入这个问题更快地得到答案。
  • 或至少使用小样本输入、预期输出和当前输出(包括任何错误消息(精确文本))来编辑您的问题。祝你好运。
  • sed 文件将\1 捕获到\5,因此对\6\7\8\9 的引用是错误的。这就是错误消息所说的,真的。
  • @JonathanLeffler 你对 6-9 做什么?
  • 要么在 s/// 正则表达式的左侧添加更多捕获,要么不在其右侧引用它们(即使它是 s||| 命令)。如果你必须用sed 来做,你必须把这些东西写出来。使用awk 可能会做得更好,它可以合理可靠地以逗号分隔(除非任何字段中都嵌入了逗号——这也让sed 令人头疼),或者使用具有“适当”支持的语言对于 CSV 格式数据:Perl 有一个可以安装的 Text::CSV 模块,Python 有一个可以使用的 CSV 模块。

标签: sql linux unix ubuntu sed


【解决方案1】:

我不会为此使用sed。看起来 Python 标准库中的 csv 模块很可能可以解析您的输入,而您的编码要少得多,而且比您将在 sed 中编写的任何内容都更加健壮。 p>

这是一个简单的程序,它将读取您所描述的 CSV 文件,假设您有一个与列名列表匹配的标题行(按您希望它出现在 SQL 中的字面引用)并将其写入如您所描述的一系列 INSERT INTO 语句:

#!python
# For Python version 2.x > 2.6
import csv, sys
form = 'INSERT INTO `Schedule` {0} VALUES ({1});\n'

if __name__ == '__main__':
    num_args = len(sys.argv[1:])
    if num_args < 1:
        print >> sys.stderr, "Must supply input filename"
        sys.exit(1)

    with open(sys.argv[1], 'r') as infile:
        reader = csv.reader(infile)
        header = next(reader)
        data = list()
        for row in reader:
            data.append(row)

    with open('./csvdata.sql', 'w') as outfile:
        for each in data:
            outfile.write(form.format(', '.join(header), ', '.join(each)))

只需做一些额外的工作,您就可以为输入和输出文件名添加对命令行参数的支持,并将其变成一个通用实用程序,用于为具有适当标题行的任何 CSV 生成 SQL INSERT 语句(您可以在其中指定表使用命令行选项插入其中)。

稍加努力,您可以省略标题并让一些 Python 动态地从数据库中提取列名列表(通过任何 Python DBAPI 驱动程序执行类似SELECT * FROMsometableWHERE 1=0 的查询并读取结果集对象.description 属性)。

为了简单起见,我只将其分成两个单独的循环...将outfile.write() 合并到第一个循环中以消除data.append,并且您可以处理任意大文件的恒定内存开销。

直接添加 SQL 处理并弄清楚如何使用.executemany(),您可以一次性执行操作(CSV -> 您的 SQL RDBMS),而无需中间的 .sql 文本文件。 (为此,您可能还必须在 CSV 读取循环中添加一些类型处理)。但是,这可能比单个 SQL INSERT 语句的文本文件更有效。 (例如,这些 Python DBAPI 驱动程序的一部分可能位于平台原生 C 二进制文件中,而不是通过 Python 调度对.executemany() 中的每一行执行)。

这是适用于 Python 3.x 的脚本版本,并实现了稍微改进的参数处理。这里输出文件是第一个参数,所有其他参数都是输入文件:

#!python
#!/usr/bin/env python3
## Python 3.x
import csv, sys

usage='%s outfile infile [infile2 ...]' % sys.argv[0]

form = 'INSERT INTO `Schedule` {0} VALUES ({1});\n'

if __name__ == '__main__':
    num_args = len(sys.argv[1:])
    if num_args < 1:
        print ("Must supply input filename", file=sys.stderr)
        sys.exit(1)

    data = list()
    for each in sys.argv[2:]:
        try:
            with open(each, 'r') as infile:
                reader = csv.reader(infile)
                header = next(reader)
                for row in reader:
                    data.append(row)
        except EnvironmentError as e:
            print('Warning %s: unable to read data from %s' % (e, each), file=sys.stderr)

    try:
        with open(sys.argv[1], 'w') as outfile:
            for each in data:
                outfile.

write(form.format(', '.join(header), ', '.join(each))) 除了 EnvironmentError 为 e: print('错误: %s: 无法将结果写入 %s' % (e, sys.argv[1], file=sys.stderr) sys.exit(1)

此版本还围绕文件 I/O 添加了非常简单的异常处理,将输入错误读取为“警告”并将任何输出错误视为错误。

【讨论】:

  • myinput.csv 将位于@Jim Dennis 的什么位置?另外,r 和 w 是干什么用的?
  • 在我的示例中,我将 ./myinput.csv 硬编码为文件名。你可以用你喜欢的任何东西来代替它。我将编辑该示例,以便您可以在命令行中提供它。
  • AttributeError: '_csv.reader' object has no attribute 'next'
  • 您使用的是什么版本的 Python?它适用于我的 2.7.9 副本;最迟从 2.6 开始,我所做的一切都没有改变?
  • 3.4
【解决方案2】:

这将是 cmets,但它过于复杂,无法再在评论中呈现,所以它成为一个答案。

sed 脚本设置 5 个捕获,然后尝试引用捕获 1-9。错误消息告诉您无法引用未捕获的内容。

原来的sed 脚本似乎是:

s|\([A-Z]*\),\([A-Z]*\),\([A-Za-z0-9' ()\.\!-]*\),\([A-Za-z0-9' ()\.\!-]*\),\([0-9]*\)|INSERT INTO `Schedule` (`ID`,`YEAR`,`NUMBER`,`TeamR1`,`TeamR2`, `TeamR3`, `TeamB1`, `TeamB2`, `TeamB3`) VALUES ('\1', '\2', '\3', '\4', '\5', '\6', '\7', '\8', '\9');|g

其中的“匹配”部分是:

|\([A-Z]*\),\([A-Z]*\),\([A-Za-z0-9' ()\.\!-]*\),\([A-Za-z0-9' ()\.\!-]*\),\([0-9]*\)|
  ^          ^          ^                         ^                         ^
  1          2          3                         4                         5

示例数据似乎是 9 个整数,以逗号分隔。这似乎与所使用的列标题不太相似,但这可能是一个单独的讨论。如果 CSV 数据足够简单以至于字段中不包含逗号或单引号(在这种情况下),那么您可以更简单地处理它:

s|\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\)|INSERT … VALUES ('\1', '\2', '\3', '\4', '\5', '\6', '\7', '\8', '\9');|

如果您的 sed 版本支持 ERE(扩展正则表达式),您可以将其简化为:

s|([^,]*\),([^,]*\),([^,]*\),([^,]*\),([^,]*\),([^,]*\),([^,]*\),([^,]*\),([^,]*\)|INSERT … VALUES ('\1', '\2', '\3', '\4', '\5', '\6', '\7', '\8', '\9');|

您可能需要使用 -r (GNU sed) 或 -E (BSD sed) 来启用 ERE。

这可以帮助您解决sed 脚本中的技术问题。这是一种处理数据的可怕方式。对于示例数据,您可以使用awk 来处理这些行:

awk -F, 'BEGIN { i1 = "INSERT INTO `Schedule` (`ID`,`YEAR`,`NUMBER`"
                 i2 = ",`TeamR1`,`TeamR2`, `TeamR3`, `TeamB1`,"
                 i3 = "`TeamB2`, `TeamB3`) VALUES"
               }
         NF==9 {printf "%s%s%s('%s','%s','%s','%s','%s','%s','%s','%s','%s');\n",
                       i1, i2, i3, $1, $2, $3, $4, $5, $6, $7, $8, $9 }
        '

awk 中还有许多其他方法可以处理此问题,例如循环等。

如果您确实需要处理 CSV 数据,请使用能够理解 CSV 数据的语言。例如,Perl 有Text::CSV(但它通常必须作为额外安装),Python 有它的csv 模块作为标准安装。这些中的任何一个都将使其更易于管理。

【讨论】:

    【解决方案3】:

    假设你的 sed 文件是正确的(我怀疑),那么你会运行

    sed -f csv2sql.sed xxx.dat > schedules.sql
    

    其中 xxx.dat 是包含您的 csv 数据的文件。

    Schedules.sql 将在您当前的工作目录中创建。

    【讨论】:

      【解决方案4】:

      你可以这样跑

      sed -f csv2sql.sed tes1.csv
      

      CSV:

      ABC,ABC,AbC9).,AbC9).,98
      

      输出:

      INSERT INTO `Schedule` (`F1`,`F2`,`F3`,`F4`,`F5`) VALUES ('ABC', 'ABC', 'AbC9).', 'AbC9).', '98');
      

      csv2sql.sed 看起来像这样

      s|\([A-Z]*\),\([A-Z]*\),\([A-Za-z0-9’ ()\.\!-]*\),\([A-Za-z0-9’ ()\.\!-]*\),\([0-9]*\)|INSERT INTO `Schedule` (`F1`,`F2`,`F3`,`F4`,`F5`) VALUES ('\1', '\2', '\3', '\4', '\5');|g
      

      【讨论】:

        猜你喜欢
        • 2012-06-05
        • 2014-06-13
        • 2019-07-09
        • 2017-09-29
        • 2023-04-09
        • 1970-01-01
        • 1970-01-01
        • 2016-08-27
        • 2020-04-02
        相关资源
        最近更新 更多