Подсчет строк в CSV-файлах, вложенных в подкаталог, с помощью команды find exec

:%s/\(\[\[\d\+[_ ]\+Week\([_ ]\+\)\(\d\+\)\)\]\],\(.*\)/\1|Week\2\3,\4]]/

Вы все еще можете улучшить это выражение, дополнив его \s*там, где это уместно, чтобы лучше обнаруживать несоответствия, которые неизменно возникают в тексте, набираемом вручную.

Некоторые проблемы с предлагаемым вами решением:

  • Регулярное выражение:\(2018[_\s]Week[_\s]\d\d\),\s\(\d+\w+\)не совпадает, потому что:

    • Предопределенные классы символов, экранированные обратной косой чертой -, нельзя использовать в определенных пользователем -классах символов, разделенных []. [_\s]соответствует символу подчеркивания, обратной косой черты или символу s. В таких ситуациях вы можете использовать _\|\s.
    • Символ +должен быть экранирован, чтобы его особое значение в качестве квантора «1 или более» было активным. В противном случае он соответствует буквальному знаку +.
    • Части ,\s\(\d+\w+\)предшествует последовательность, соответствующая \]\]в сопоставляемом тексте, но \]\]отсутствует в шаблоне.
  • Не принимая во внимание проблему с обратной косой чертой в строке подстановки, вы пытаетесь завершить результирующую строку с помощью ]], но вы сопоставили только до части, которая указывает день после запятой, используя \d\+\w\+. Это означает, что если подстановка прошла успешно, ваши строки будут заканчиваться текстом вида :29th]] April through 5th May,имея последовательность ]], которая должна была заканчивать строку где-то посередине.

  • Строка замены:\[\[\1|\1\2\]\]не является регулярным выражением, поэтому такие символы, как [и ], экранировать не нужно.

  • Кроме того, \d\+\w\+, хотя и не является ошибочным, является избыточным, поскольку \wуже охватывает все, что делает \d, и то, как вы указали его контекст с предыдущей частью выражения, всегда соответствует таким вещам, как 9thи т. д. и никогда не соответствует чему-либо плохому.

РЕДАКТИРОВАТЬ :Очень хорошее предложение от @user1133275: (с некоторыми изменениями )использовать запятую в группе захвата, которая следует за ней в исходном решении, а также изменить строки, где не указан дневной интервал, т.е. нет "xth to yth":

:%s/\(\[\[\d\+[_ ]\+Week\([_ ]\+\)\(\d\+\)\)\]\]\(,.*\)\?/\1|Week\2\3\4]]/

@user1133275 не дал ответа, поэтому я разместил результаты нашего обсуждения в разделе комментариев к этому ответу здесь. Если они решат поместить это в ответ, и я буду уведомлен, я удалю это редактирование, чтобы кредиты могли перейти к автору базовой идеи.

3
28.02.2021, 12:33
2 ответа

Если написать

find... -exec foo | bar \;

вертикальная черта интерпретируется вашей оболочкой до вызова find. Левая часть результирующего конвейера — это find... -exec foo, что, очевидно, приводит к ошибке «отсутствует аргумент для `-exec»; правая сторона трубопровода bar.

Защита вертикального стержня от корпуса, как в

find... -exec foo \| bar \;

не помогает, потому что первая лексема после -execинтерпретируется findкак команда и все последующие лексемы, до (, но не включая ), ;или +терминатор, принимаются в качестве аргументов этой команды.

См. Понимание опции -exec команды `find`для подробного объяснения.

Чтобы использовать конвейер с -exec, вам нужно вызвать оболочку. Например:

find./tmp/*/ -name '*.csv' -exec sh -c '
  printf "%s %s\n" "$(tail -n +2 "$1" | wc -l)" "$1"' mysh {} \;

Затем, чтобы избежать ошибки «слишком длинный список аргументов», ./tmp/*/можно переписать как

find./tmp -path './tmp/*/*'...

или, точнее, также исключить tmpскрытые подкаталоги (, как ./tmp/*/скорее всего сделал бы по умолчанию ), как

find./tmp -path './tmp/.*' -prune -o -path './tmp/*/*'...

Наконец, вы можете использовать -exec... {} +более быстрый вариант, который позволяет избежать вызова оболочки для каждого найденного файла. Например, с awkвместо tailиwc:

find./tmp -path './tmp/.*' -prune -o -path './tmp/*/*' \
  -name '*.csv' -exec awk '
    BEGIN { skip = 1 }
    FNR > skip { lc[FILENAME] = (FNR - skip) }
    END { for (f in lc) print lc[f],f }' {} +

(Обратите внимание, что awkтакже учитывает те искаженные строки, которые не заканчиваются символом новой строки, а wcне учитывает ).

5
18.03.2021, 22:27

если все, что вам нужно, это по существу вычесть 1 из каждого wc -l, это очень просто и чисто:

find [whatever you want] -exec wc -l {} + | perl -pe 's/(\d+)/$1-1/e'
0
18.03.2021, 22:27

Теги

Похожие вопросы