【问题标题】:Concatenate multiple CSV files in a windows machine by running a shell command through python通过 python 运行 shell 命令来连接 Windows 机器中的多个 CSV 文件
【发布时间】:2021-03-31 15:12:00
【问题描述】:

目标:在特定条件下连接多个 CSV 文件。

  • 条件:Windows 10 机器 + 任何 shell 命令(例如,awk)+ Python
  • 免责声明:我知道有很多方法可以做到这一点,包括:使用 pandas 数据帧连接;或者使用python的shutil;或者在 shell 上直接使用系统命令(例如,awk)而不使用 python。但同样,我想要一个完全符合上述条件的答案。

这是我当前正在尝试运行的一个 shell 命令示例(如果我直接在 shell 上运行,它就可以工作)

$ awk "(NR == 1) || (FNR > 1)" "..\\results\\raw*.csv" > "..\\results\\consolidated.csv"

这就是我通过 python 的subprocess 运行它的方式:

src_files = os.path.join('..', 'results', 'raw*.csv')
dest_file = os.path.join('..', 'results', 'consolidated.csv')

result = subprocess.run('awk "(NR == 1) || (FNR > 1)" "{}" > "{}"'.format(src_files, dest_file),
                        shell=True,
                        capture_output=True)

但是,我不断收到以下错误:

print(result.stderr)

b"awk: fatal: cannot open file `..\\results\\raw*.csv' for reading (No such file or directory)\n"

我应该指出,如果我在 Unix 机器上运行这个 python 代码,它就可以工作(如果格式 {} 周围的引号被删除)。 python 代码不能在 Windows 机器上运行。

因此,我相信这可能与正确转义命令字符串有关,因为当我指定某些文件的名称时,此命令有效。尽管如此,我还是无法找到转义 * 符号并完成所有这些工作的正确方法。

PS:为工作示例生成数据

for i in range(1, 4):
    pd.DataFrame([[i*1, i*2, i*3]], columns=['a', 'b', 'c']).to_csv(os.path.join('results', 'raw-{}.csv'.format(i)), index=False, sep=';')

PS2:使用的文件夹结构

|-- script_folder/
|   |-- consolidation_script.py
|-- results/
|   |-- raw-1.csv
|   |-- raw-2.csv
|   |-- raw-3.csv

【问题讨论】:

  • 尝试使用shlex 模块来引用参数。
  • 嗨@mar​​tineau,刚刚尝试了shlex.quote(),但它仍然没有工作,同样的错误。 shlex.join() 看起来很有希望,但它只在 python 3.9 中可用,我仅限于 python 3.7。
  • 我提出了一个愚蠢的问题,但你为什么需要 awk 来做到这一点?命令“type ..\results\raw*.csv > ..\results\consolidated.csv”不符合您的要求?
  • 您尝试运行的命令是'awk "(NR == 1) || (FNR > 1)" "..\\results\\raw*.csv" > "..\\results\\consolidated.csv"',这对于subprocess.run() 来说太复杂了——例如,重定向输出是通过stdout=None 关键字参数完成的。我同意@Ian 的观点,这可能最好完全使用 Python 完成,而不是使用像 awk 这样的外部程序。
  • @IanMcGowan 我不一定需要awk,它可以是任何shell命令。例如,有些人喜欢使用sed。只是当我想通过shell命令合并.csv文件并保留header时,我最习惯使用awk

标签: python windows shell awk


【解决方案1】:

考虑在定位当前脚本路径后使用cwd 参数更改工作目录。是的,os.path.dirname() 被调用两次以获取当前目录的父目录。

import os, subprocess

# RETRIEVE CURRENT DIRECTORY OF SCRIPT
cd = os.path.dirname(os.path.abspath(__file__))

arglist = ["awk", "(NR == 1) || (FNR > 1)", "raw*.csv", ">", "consolidated.csv"]

result = subprocess.run(arglist,
                        cwd = os.path.join(os.path.dirname(cd), 'results'),
                        shell=True,
                        capture_output=True)

【讨论】:

  • 这看起来很有希望。我一下班回到我的机器上就试试。您认为这仍然可以与相对路径一起使用吗?
  • 在某种程度上,这是一个相对路径解决方案,因为它从脚本所在的目录工作。只有子文件夹、results 和 .csv 文件名是硬编码的.
  • 您好 Parfait,您的解决方案效果很好。但是,它需要在这里cwd = os.path.join(os.path.dirname(cd), 'results') 进行小幅调整。应该是cwd = os.path.join(cd, 'results')。请您编辑您的答案,以便我接受吗?
  • 嗯...正如我提到的,您需要拨打os.path.dirname() 两次。 Python 脚本是否与results 文件夹处于同一级别,而不是如上所示的script_folder 内?
  • 哎呀,你是对的。我是一个犯错的人,因为我实际上使用的是在笔记本上方一层启动的 jupyter 服务器,因此它不需要第二次dir_name 调用。无论如何,您的回答完全符合我在问题中描述的情况。非常感谢!
猜你喜欢
  • 2013-11-22
  • 1970-01-01
  • 1970-01-01
  • 2011-07-12
  • 1970-01-01
  • 1970-01-01
  • 2013-01-31
  • 2019-02-05
  • 1970-01-01
相关资源
最近更新 更多