Запустите sed для подмножества аргументов одновременно - PullRequest
1 голос
/ 17 января 2020

В настоящее время я выполняю sed в подпроцессе python, однако получаю сообщение об ошибке:

"OSError: [Errno 7] Argument list too long: 'sed'"

Код Python:

subprocess.run(['sed', '-i',
                '-e', 's/#/pau/g',
                *glob.glob('label_POS/label_phone_align/dump/*')], check=True)

Где / В каталоге dump / содержится ~ 13 000 файлов. Мне сказали, что мне нужно запустить команду для подмножеств списка аргументов, но я не могу найти, как это сделать.

Ответы [ 2 ]

1 голос
/ 17 января 2020

Кто бы вам ни сказал, это, вероятно, означало, что вам нужно разделить глобус и запустить несколько отдельных команд:

files = glob.glob('label_POS/label_phone_align/dump/*')
i = 0
scale = 100
# process in units of 100 filenames until we have them all
while scale*i < len(files):
    subprocess.run(['sed', '-i',
            '-e', 's/#/pau/g',
            *files[scale*i:scale*(i+1)]], check=True)
    i += 1

, а затем объединить все эти выходные данные, как вам нужно, после факта. Я не знаю, сколько входных данных может принять команда sed из командной строки, но это, очевидно, меньше 13 000. Вы можете продолжать изменять scale, пока не произойдет ошибка.

0 голосов
/ 17 января 2020

Пожалуйста, прокрутите вниз до конца этого ответа, чтобы найти решение, которое я рекомендую для вашей конкретной проблемы c. Здесь есть некоторая предыстория для контекста и / или будущих посетителей, сталкивающихся с другими ошибками «слишком длинный список аргументов».

Системный вызов exec() имеет ограничение размера; вы не можете передать более ARG_MAX байтов в качестве аргументов процесса, где значение этой системной константы обычно запрашивается командой getconf ARG_MAX в современных системах.

import glob
import subprocess

arg_max = subprocess.run(['getconf', 'ARG_MAX'],
    text=True, check=True, capture_output=True
    ).stdout.strip()
arg_max = int(arg_max)

cmd = ['sed', '-i', '-e', 's/#/pau/g']
files = glob.glob('label_POS/label_phone_align/dump/*')
while files:
    base = sum(len(x) for x in cmd) + len(cmd)
    for l in range(len(files)):
        base += 1 + len(files[l])
        if base > arg_max:
            l -= 1
            break
    subprocess.run(cmd + files[0:l+1], check=True)
    files = files[l+1:]

Конечно, xargs Команда уже делает именно это для вас.

import subprocess
import glob

subprocess.run(
    ['xargs', '-r', '-0', 'sed', '-i', '-e', 's/#/pau/g'],
    input=b'\0'.join([x.encode() for x in glob.glob('label_POS/label_phone_align/dump/*') + ['']]),
    check=True)

Однако в вашем случае может быть достаточно просто удалить длинный путь. Вы повторяете label_POS/label_phone_align/dump/ перед каждым именем файла в массиве аргументов.

import glob
import subprocess
import os

path = 'label_POS/label_phone_align/dump'
files = [os.path.basename(file)
    for file in glob.glob(os.path.join(path, '*'))]
subprocess.run(
    ['sed', '-i', '-e', 's/#/pau/g', *files],
    cwd=path, check=True)

В конце концов, возможно, предпочтете чисто Python решение.

import glob
import fileinput

for line in fileinput.input(glob.glob('label_POS/label_phone_align/dump/*'), inplace=True):
    print(line.replace('#', 'pau'))
...