Следующий скрипт (который создан частично в образовательных целях, и именно поэтому здесь используется не только Perl†, но также awk
и sed
‡)...
† Версия Perl — 5.34
‡ awk
и sed
— это те, которые поставляются с macOS.
thisscript input.md output.txt
sed 's/[[:space:]]-[[:space:]]/---/g' $1 |
sed 's/[[:space:]]\{0,1\}—[[:space:]]\{0,1\}/---/g' |
sed 's/\\\*/†/g' |
sed 's/*///g' |
sed 's/\\\././g' |
sed 's/…/.../g' |
awk 'BEGIN{RS = "";ORS = "\n "}1' |
fold -s -w 72 |
perl -C -lpe '
BEGIN{ $/ = "\n " }
s/ (\w(?: \w)*)\r?\n/\n$1 /g;
s/ (\w{2,}(?: \w)+[?.!])\s*$/\n$1/;
s/ (\w+)\r?\n(?=---)/\n$1/g;
s/^ */ /
' |
sed 's/[[:space:]]+//g' > $2
заключается в преобразовании типичного текста Markdown (учитывая, что текст достаточно простой, скажем, детской книжки о пиратах) во что-то более приятное на мой вкус.
Тест 1
Введите текст:
Lorem ipsum dolor sit amet, consectetur adipiscing elit, satoru do a eiusmod tempor incididunt ut labore et dolore magna aliqua. Do b c quis autem vel eum iure reprehenderit qui in ea voluptate velit esse ---minim a b veniam, quis nostrud exercitation ullamco laboris d. Lorem ipsum dolor sit amet, consectetur adipiscing elit, satoru do a eiusmod tempor incididunt ut labore et dolore magna aliqua. Do b c quis autem vel eum iure reprehenderit qui in ea voluptate velit esse ---minim a b veniam, quis nostrud exercitation ullamco laboris d.
Выходной текст:
Lorem ipsum dolor sit amet, consectetur adipiscing elit, satoru do a eiusmod tempor incididunt ut labore et dolore magna aliqua. Do b c quis autem vel eum iure reprehenderit qui in ea voluptate velit esse---minim a b veniam, quis nostrud exercitation ullamco laboris d. Lorem ipsum dolor sit amet, consectetur adipiscing elit, satoru do a eiusmod tempor incididunt ut labore et dolore magna aliqua. Do b c quis autem vel eum iure reprehenderit qui in ea voluptate velit esse---minim a b veniam, quis nostrud exercitation ullamco laboris d.
Как вы, возможно, заметили, часть Perl отвечает за перемещение любого однобуквенного слова на следующую строку. Есть и другие вещи, которые делает часть Perl, но для целей этого вопроса о Unix и Linux они не имеют значения. Нам нужно только знать, работает ли часть Perl или нет.
Тест 2
Введите текст:
Lorem ipsum dolor sit amet, consectetur adipiscing elit, satoru do a eiusmod tempor incididunt ut labore et dolore magna aliqua. Do b c quis autem vel eum iure reprehenderit qui in ea voluptate velit esse---minim a b veniam, quis nostrud exercitation ullamco laboris d. Lorem ipsum dolor sit amet, consectetur adipiscing elit, satoru do a eiusmod tempor incididunt ut labore et dolore magna aliqua. Do b c quis autem vel eum iure reprehenderit qui in ea voluptate velit esse---minim a b veniam, quis nostrud exercitation ullamco laboris d.
Выходной текст должен быть таким же, как выходной текст первого теста, но это не так:
Lorem ipsum dolor sit amet, consectetur adipiscing elit, satoru do a eiusmod tempor incididunt ut labore et dolore magna aliqua. Do b c quis autem vel eum iure reprehenderit qui in ea voluptate velit esse---minim a b veniam, quis nostrud exercitation ullamco laboris d. Lorem ipsum dolor sit amet, consectetur adipiscing elit, satoru do a eiusmod tempor incididunt ut labore et dolore magna aliqua. Do b c quis autem vel eum iure reprehenderit qui in ea voluptate velit esse---minim a b veniam, quis nostrud exercitation ullamco laboris d.
Как вы могли заметить, однобуквенные слова не были перемещены, то есть Perl не участвовал в обработке текста. Насколько я понимаю (я просто сказал, что нужно изучать Perl), это потому, что строка
BEGIN{ $/ = "\n " }
следует отрегулировать так, чтобы он соответствовал абзацам, разделенным пробелами. Но мои попытки, такие как эта:
BEGIN{ $/ = "\n |\n\n" }
не помогло.
Что я делаю не так?
@terdon Желаемый результат (то есть результат второго теста) должен быть точно таким же, как результат первого теста.
Да, но как? Первый из них специально обрабатывает однобуквенные слова в конце строки. Во втором примере всего две строки, так что же отличает однобуквенные слова, которые вы хотите переместить на новую строку, от однобуквенных слов, которые вы не хотите переносить на новую строку? Посмотрите примеры в моем предыдущем комментарии. Как мы можем узнать, какие из них поставить на новую строку, а какие оставить?
@terdon Но в сценарии есть fold -s -w 72
перед perl
. Если я правильно понимаю, perl
должен принимать вывод fold
, тогда как вывод fold
представляет собой текст, который жестко перенесен в столбец 72. Может я что-то неправильно понял?
Вы правильно поняли, но посмотрите на вывод fold
. Просто закомментируйте все, что стоит после fold
в вашем скрипте, и запустите его. Вы увидите, что вместо Do b c\n
у вас есть Do b c quis\n
, так как же нам узнать, нужно ли добавить \n
после c
? Выходные данные существенно отличаются от двух входных примеров, поэтому вы не можете относиться к ним одинаково.
@terdon Понятно, спасибо, но это не ответ на вопрос, почему скрипт не перемещает слово a
в satoru do a
(первая строка каждого абзаца). Насколько я понимаю, это небольшое изменение изменит форму следующих строк, и тогда вместо Do b c quis\n
будет Do b c\n
, и тогда мы можем ожидать тот же результат, что и в первом тесте. Не так ли?
У вас есть ответ, объясняющий, что: $/
не может быть регулярным выражением, а только определенной строкой, поэтому ваш \n |\n\n
не означает «новую строку, за которой следует пробел ИЛИ две последовательные новые строки», он означает «новую строку, за которой следует пробел». , затем |
, а затем две последовательные новые строки».
@terdon Да, но это не сработает, даже если я заменю строку BEGIN{ $/ = "\n " }
на BEGIN{ $/ = "\n\n" }
. Теперь кажется, что он не содержит регулярных выражений, но все равно не работает.
Почему \n\n
сработает? Разве вы не смотрели на вывод fold
, как я предлагал выше? К тому времени, как вы доберетесь до команды \n\n
, perl
исчезнет, ваш awk
удалит их. Пожалуйста, задайте новый вопрос здесь или в Unix & Linux и с самого начала объясните, для чего вам действительно нужен скрипт. На этот вопрос о том, почему подход $/
не удался, получен ответ.
@terdon Хорошо, извини, ты прав, и замена BEGIN{ $/ = "\n " }
на BEGIN{ $/ = "\n\n" }
была глупой идеей. Но я так и не понимаю, почему скрипт не перемещается a
в satoru do a
.
Давайте продолжим обсуждение в чате.
От perldoc perlvar
(см.
https://perldoc.pl/perlvar#$/)
$/ The input record separator, newline by default. This influences
Perl's idea of what a "line" is. Works like awk's RS variable,
including treating empty lines as a terminator if set to the
null string (an empty line cannot contain any spaces or tabs).
You may set it to a multi-character string to match a
multi-character terminator, or to "undef" to read through the
end of file. Setting it to "\n\n" means something slightly
different than setting to "", if the file contains consecutive
empty lines. Setting to "" will treat two or more consecutive
empty lines as a single empty line. Setting to "\n\n" will
blindly assume that the next input character belongs to the next
paragraph, even if it's a newline.
local $/; # enable "slurp" mode
local $_ = <FH>; # whole file now here
s/\n[ \t]+/ /g;
Remember: the value of $/ is a string, not a regex. awk has to
be better for something. :-)
Обратите внимание на последнее предложение.
Скрипт по-прежнему не работает, но я думаю, было бы справедливо сказать, что на вопрос дан ответ, так что спасибо.
Можете ли вы показать нам желаемый результат? Как будет выглядеть правильный результат? Хотите, чтобы все однобуквенные слова начинались с новой строки? В первом примере вы перемещаете любые однобуквенные слова, находящиеся в конце строки, на новую строку. Хорошо, какие из них следует переместить во втором примере? Как ваш сценарий узнает, что нужно разделить строку на
a
слова «satoru do a», но не разделить ее наc
слова «Do b c quis» илиb
слова «minim a b»?