Цель: объединить несколько файлов CSV с учетом определенных условий.
awk) + Python <= 3.7.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
Привет @martineau, только что попробовал shlex.quote(), но все равно не работает, та же ошибка. shlex.join() кажется многообещающим, но он доступен только в Python 3.9, а я ограничен Python 3.7.
Глупый вопрос от меня, но зачем вам awk для этого? Команда «тип ..\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, это может быть любая команда оболочки. Например, некоторые люди любят использовать sed. Просто я чаще всего использую awk, когда хочу объединить .csv файлы с помощью команды оболочки и сохранить заголовок.
Пожалуйста, опубликуйте полный код командной строки, который работает вне Python.
Очень легко пропустить заголовки CSV с помощью включенного в Python модуля csv (или даже вручную в большинстве случаев).
@IanMcGowan Конечно, как я уже сказал в вопросе, я могу просто объединить кадры данных с помощью pandas или использовать python shutil. Есть множество способов сделать это, но задача здесь состоит в том, чтобы сделать это с учетом условий, которые я описываю в вопросе.
@Parfait В вопрос добавлена команда оболочки.






Рассмотрите возможность использования аргумента 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)
Это кажется многообещающим. Я попробую, как только вернусь к своей машине с работы. Как вы думаете, это все еще может работать с относительными путями?
В некотором смысле это решение с относительным путем, поскольку оно работает из каталога, в котором находится скрипт. Жестко запрограммированы только имена подпапок, результатов и файлов .csv.
Привет, Парфе, ваше решение сработало отлично. Но здесь требуется небольшая корректировка 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 не требовался. В любом случае, ваш ответ идеально подходит к ситуации, которую я описал в вопросе. Большое спасибо!
Рад слышать и рад помочь! Удачного кодирования!
Попробуйте использовать модуль shlex, чтобы процитировать аргументы.