s /// возвращает с места новую строку - PullRequest
0 голосов
/ 15 сентября 2018

Я пытаюсь использовать Perl для изменения порядка содержимого файла md5.Для каждой строки я хочу имя файла без пути, а затем хеш.Лучшая команда, которую я придумал:

$ perl -pe 's|^([[:alnum:]]+).*?([^/]+)$|$2 $1|' DCIM.md5

Входной файл (DCIM.md5) создается md5sum в Linux.Это выглядит так:

e26ff03dc1bac80226e200c0c63d17a2  ./Path1/IMG_20150201_160548.jpg
01f92572e4c6f2ea42bd904497e4f939  ./Path 2/IMG_20150204_190528.jpg
afce027c977944188b4f97c5dd1bd101  ./Path3/Path 4/IMG_20151011_193008.jpg
  1. Хэш соответствует первой группе ([[:alnum:]]+) в регулярном выражении
    .
  2. Затем пробелы и путь кфайл
    соответствует .*?.
  3. Тогда имя файла соответствует ([^/]+).
  4. Выражение заключено в ^ (здесь, по-видимому, нет необходимости) и $.Без $ выражение не выводит то, что я ожидаю.
  5. Я использую | вместо / в качестве разделителя, чтобы избежать экранирования в путях к файлам.

Эта команда возвращает:

IMG_20150201_160548.jpg
 e26ff03dc1bac80226e200c0c63d17a2IMG_20150204_190528.jpg
 01f92572e4c6f2ea42bd904497e4f939IMG_20151011_193008.jpg
 afce027c977944188b4f97c5dd1bd101IMG_20151011_195133.jpg

Соответствие правильное, последовательность вывода правильная (имя файла без пути, затем хэш), но интервал - нет: после имени файла есть новая строка.Я ожидаю этого после хэша, например:

IMG_20150201_160548.jpg e26ff03dc1bac80226e200c0c63d17a2
IMG_20150204_190528.jpg 01f92572e4c6f2ea42bd904497e4f939
IMG_20151011_193008.jpg afce027c977944188b4f97c5dd1bd101

Мне кажется, что моя команда выводит символ новой строки, но я не знаю, как изменить это поведение.Или, возможно, проблема в оболочке, а не в команде?

Наконец, некоторая информация о версии:

$ perl -version
This is perl 5, version 22, subversion 1 (v5.22.1) built for i686-linux-gnu-thread-multi-64int
(with 69 registered patches, see perl -V for more detail)

Ответы [ 4 ]

0 голосов
/ 16 сентября 2018

Альтернативное решение, которое использует множество концепций из других ответов и комментариев ...

$ perl -pe 's|(\p{hex}+).*?([^/]+?)$|$2 $1|' DCIM.md5

... и объяснений.

После изучения всех ответов и попытки выяснитьЯ решил, что основа проблемы в том, что [^/]+ является жадным .Его жадность заставляет его захватывать новую строку;он игнорирует якорь $.

Мне было трудно это понять, поскольку я много раз анализировал с использованием sed перед использованием Perl, и даже жадный шаблон не будет захватывать новую строку вsed.Надеюсь, этот пост поможет тем, кто (привык к sed, как я), также задается вопросом (как и я), почему $ не действует "так, как я ожидаю".

Мыможно увидеть «жадную» проблему, попробовав то, что я опубликую как другой, альтернативный ответ.

Напишите файл:

$ cat > DCIM.md5<<EOF
> e26ff03dc1bac80226e200c0c63d17a2  ./Path1/IMG_20150201_160548.jpg
> 01f92572e4c6f2ea42bd904497e4f939  ./Path 2/IMG_20150204_190528.jpg
> afce027c977944188b4f97c5dd1bd101  ./Path3/Path 4/IMG_20151011_193008.jpg
> EOF

Избавьтесь от жадного [^/]+, изменив егодо [^/]+?.Разбор.

$ perl -pe 's|([[:alnum:]]+).*?([^/]+?)$|$2 $1|' DCIM.md5
IMG_20150201_160548.jpg e26ff03dc1bac80226e200c0c63d17a2
IMG_20150204_190528.jpg 01f92572e4c6f2ea42bd904497e4f939
IMG_20151011_193008.jpg afce027c977944188b4f97c5dd1bd101

Требуемый вывод выполнен.

принятый ответ , @Shawn,

$ perl -lpe 's|^([[:alnum:]]+).*?([^/]+)$|$2 $1|' DCIM.md5

в основном изменяет $якорь, чтобы вести себя так, как ожидал бы sed человек.

Ответ @CrafterKolyan заботится о жадном [^/] захвате новой строки, говоря, что у вас не может быть косой черты или новой строки.Этот ответ все еще нуждается в привязке $, чтобы предотвратить следующую ситуацию

1) .* захватывает пустую строку ( 0 или более любого символа)

2) [^/\n]+ захватывает ..

Ответ @Borodin использует совсем другой подход, но это отличная концепция.

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

Наконец, если кто-то хочет следовать Perl модель программирования , вот еще один вариант.

$ perl -pe 's|([[:xdigit:]]+).*?([^/]+?)(\n\|\Z)|$2 $1$3|' DCIM.md5

PS Потому что sed не совсем похоже на perl ( без жадных подстановочных знаков ,) вот пример sed, который показывает поведение, которое я обсуждаю.

$ sed 's|^\([[:alnum:]]\+\).*/\([^/]\+\)$|\2 \1|' DCIM.md5

Это в основном «прямой перевод» выражения perl , за исключением для дополнительных '/' перед [^/].Надеюсь, это поможет тем, кто сравнивает sed и perl.

0 голосов
/ 15 сентября 2018

используйте [^/\n] вместо [^/]:

perl -pe 's|^([[:alnum:]]+).*?([^/\n]+)$|$2 $1|' DCIM.md5

0 голосов
/ 15 сентября 2018

Подстановка оставляет вам необходимость писать шаблон регулярных выражений, который соответствует всему, что вы не хотите, а также всему, что вы делаете.Обычно гораздо лучше подобрать именно те детали, которые вам нужны, и построить из них еще одну строку

Примерно так:

for ( <> ) {
    die unless m< (\w++) .*? ([^/\s]+) \s* \z >x;
    print "$2 $1\n";
}

или если у вас необходимо иметь однострочник

perl -ne 'die unless m< (\w++) .*? ([^/\s]+) \s*\z >x; print "$2 $1\n";' myfile.md5

выход

IMG_20150201_160548.jpg e26ff03dc1bac80226e200c0c63d17a2
IMG_20150204_190528.jpg 01f92572e4c6f2ea42bd904497e4f939
IMG_20151011_193008.jpg afce027c977944188b4f97c5dd1bd101
0 голосов
/ 15 сентября 2018

[^/]+ соответствует символам новой строки, поэтому входящие в ваш ввод являются частью $2, который ставится на первое место в вашем преобразованном $_ (И в $1 нет новой строки, поэтому в конце * нет символа новой строки) 1005 * ...)

Решение: прочитайте опцию -l из perlrun . В частности:

-l [octnum] включает автоматическую обработку конца строки. У этого есть два отдельных эффекта. Во-первых, он автоматически разбивает $ / (разделитель входных записей) при использовании с -n или -p. Во-вторых, он присваивает $ \ (разделителю выходной записи) значение octnum, чтобы в любом операторе печати этот разделитель был добавлен обратно. Если октнум опущен, устанавливает $ \ текущее значение $ /.

...