Объедините несколько файлов CSV на компьютере с Windows, запустив команду оболочки через python

Цель: объединить несколько файлов CSV с учетом определенных условий.

  • Условия: компьютер с Windows 10 + ЛЮБАЯ команда оболочки (например, awk) + Python <= 3.7.
  • Отказ от ответственности: я знаю, что есть много способов сделать это, включая: использование конкатенации фреймов данных pandas; ИЛИ с помощью питона shutil; ИЛИ с помощью системной команды (например, awk) напрямую без python в оболочке. Но опять же, я хочу получить ответ, который точно соответствует указанным выше условиям.

Вот пример команды оболочки (которая работает, если я запускаю непосредственно в оболочке), которую я сейчас пытаюсь запустить.

$ 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"

Я должен отметить, что если я запускаю этот код Python на машине Unix, он работает (если убрать кавычки вокруг формата {}). Код 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, чтобы процитировать аргументы.

martineau 21.12.2020 22:46

Привет @martineau, только что попробовал shlex.quote(), но все равно не работает, та же ошибка. shlex.join() кажется многообещающим, но он доступен только в Python 3.9, а я ограничен Python 3.7.

xicocaio 21.12.2020 23:06

Глупый вопрос от меня, но зачем вам awk для этого? Команда «тип ..\results\raw*.csv > ..\results\consolidated.csv» не делает то, что вы хотите?

Ian McGowan 21.12.2020 23:17

Вы пытаетесь запустить команду 'awk "(NR == 1) || (FNR > 1)" "..\\results\\raw*.csv" > "..\\results\\consolidated.csv"', которая слишком сложна для subprocess.run() — например, перенаправление вывода выполняется с помощью ключевого аргумента stdout=None. Я согласен с @Ian в том, что это, вероятно, лучше всего сделать с помощью Python, а не с помощью внешней программы, такой как awk.

martineau 21.12.2020 23:25

@IanMcGowan Мне не обязательно нужен awk, это может быть любая команда оболочки. Например, некоторые люди любят использовать sed. Просто я чаще всего использую awk, когда хочу объединить .csv файлы с помощью команды оболочки и сохранить заголовок.

xicocaio 21.12.2020 23:27

Пожалуйста, опубликуйте полный код командной строки, который работает вне Python.

Parfait 21.12.2020 23:28

Очень легко пропустить заголовки CSV с помощью включенного в Python модуля csv (или даже вручную в большинстве случаев).

martineau 21.12.2020 23:29

@IanMcGowan Конечно, как я уже сказал в вопросе, я могу просто объединить кадры данных с помощью pandas или использовать python shutil. Есть множество способов сделать это, но задача здесь состоит в том, чтобы сделать это с учетом условий, которые я описываю в вопросе.

xicocaio 21.12.2020 23:40

@Parfait В вопрос добавлена ​​команда оболочки.

xicocaio 21.12.2020 23:40
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
9
156
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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)

Это кажется многообещающим. Я попробую, как только вернусь к своей машине с работы. Как вы думаете, это все еще может работать с относительными путями?

xicocaio 22.12.2020 00:25

В некотором смысле это решение с относительным путем, поскольку оно работает из каталога, в котором находится скрипт. Жестко запрограммированы только имена подпапок, результатов и файлов .csv.

Parfait 22.12.2020 16:47

Привет, Парфе, ваше решение сработало отлично. Но здесь требуется небольшая корректировка cwd = os.path.join(os.path.dirname(cd), 'results'). Должно быть cwd = os.path.join(cd, 'results'). Не могли бы вы отредактировать свой ответ, чтобы я мог его принять?

xicocaio 22.12.2020 17:21

Хммм... как я уже говорил, вам нужно вызвать os.path.dirname() дважды. Скрипт Python находится на том же уровне, что и папка results, а не внутри script_folder, как показано выше?

Parfait 22.12.2020 17:27

Упс, ты прав. Я допустил ошибку, потому что на самом деле я использовал сервер jupyter, который запускался на один уровень выше ноутбука, и, следовательно, этот второй вызов dir_name не требовался. В любом случае, ваш ответ идеально подходит к ситуации, которую я описал в вопросе. Большое спасибо!

xicocaio 22.12.2020 17:38

Рад слышать и рад помочь! Удачного кодирования!

Parfait 22.12.2020 17:47

Другие вопросы по теме