p='[:punct:]' s='[:space:]'
sed -Ee'1!{/\n/!b' -e\} \
-e's/(\n*)(.*)/ \2 \1/' \
-e"s/is[$p]?[$s]/\n&/g" \
-e"s/([^$s])\n/\1/g;1G" \
-e:c -e"s/\ni(.* )\n{3}/u\1/" \
-e"/\n$/!s/\n//g;/\ni/G" \
-e's//i/;//tc' \
-e's/^ (.*) /\1/;P;$d;N;D'
Этот бит sedпросто переносит количество isпоявлений с одной строки на другую. Он должен надежно обрабатывать столько ises на строку, сколько вы набрасываете на него, и ему не нужно буферизовать старые строки, пока он это делает - он просто сохраняет один символ новой строки для каждого is, с чем встречается, который не является частью другого слова.
В результате он будет изменять только третье вхождение в файле - и он будет иметь число в строке. Так что если файл выглядит так:
1. is is isis
2. is does
... это будет печатать ...
1. is is isis
2. us does
Сначала он обрабатывает края, вставляя пробел в начало и конец каждой строки. Это немного облегчает определение границ слов.
Затем он ищет действительные значения is, вставляя \newline до того, как все вхождения isэтого предшествуют нулю или одному знаку препинания, за которым следует пробел. Он делает еще один проход и удаляет все электронные \nстроки, которым непосредственно предшествует непробельный символ. Эти оставленные маркеры будут совпадать is.иis , но не thisили ?is.
Затем он собирает каждый маркер в конец строки - для каждого \niсовпадения в строке он добавляет линию \newline к концу строки и заменяет ее либо на, iлибо на u. Если в \nконце строки собраны 3 строки, тогда используется символ u, иначе - i. Первый раз, когда используется au, также является последним - замена запускает бесконечный цикл, который сводится к get line, print line, get line, print line,и так далее.
В конце каждого цикла цикла try он очищает вставленные пробелы, печатает только до первой встречающейся новой строки в пространстве шаблона и возвращается снова.
Я добавлю lкоманду ook в начало цикла, например:
l; s/\ni(.* )\n{9}/u\1/...
... и посмотрим, что он делает, как работает с этим входом:
hai this is linux.
hai this is unix.
hai this is mac.
hai this is unchanged is.
... так вот что он делает:
hai this \nis linux. \n$ #behind the scenes
hai this is linux. #actually printed
hai this \nis unix. \n\n$ #it builds the marker string
hai this is unix.
\n\n\n$ #only for lines matching the
\n\n\n$ #pattern - and not otherwise.
hai this \nis mac. \n\n\n$ #here's the match - 3 ises so far in file.
hai this us mac. #printed
hai this is unchanged is. #no look here - this line is never evaled
Это имеет больше смысла, может быть, с большим количеством ises в строке:
nthword()( p='[:punct:]' s='[:space:]'
sed -e '1!{/\n/!b' -e\} \
-e 's/\(\n*\)\(.*\)/ \2 \1/' \
-e "s/$1[$p]\{0,1\}[$s]/\n&/g" \
-e "s/\([^$s]\)\n/\1/g;1G;:c" \
-e "${dbg+l;}s/\n$1\(.* \)\n\{$3\}/$2\1/" \
-e '/\n$/!s/\n//g;/\n'"$1/G" \
-e "s//$1/;//tc" -e 's/^ \(.*\) /\1/' \
-e 'P;$d;N;D'
)
Это практически то же самое, но написано с помощью POSIX BRE и элементарной обработки аргументов.
printf 'is is. is? this is%.0s\n' {1..4} | nthword is us 12
... получает ...
is is. is? this is
is is. is? this is
is is. is? this us
is is. is? this is
... и если я включу ${dbg}:
printf 'is is. is? this is%.0s\n' {1..4} |
dbg=1 nthword is us 12
... мы можем смотреть это повторяться ...
\nis \nis. \nis? this \nis \n$
is \nis. \nis? this \nis \n\n$
is is. \nis? this \nis \n\n\n$
is is. is? this \nis \n\n\n\n$
is is. is? this is
\nis \nis. \nis? this \nis \n\n\n\n\n$
is \nis. \nis? this \nis \n\n\n\n\n\n$
is is. \nis? this \nis \n\n\n\n\n\n\n$
is is. is? this \nis \n\n\n\n\n\n\n\n$
is is. is? this is
\nis \nis. \nis? this \nis \n\n\n\n\n\n\n\n\n$
is \nis. \nis? this \nis \n\n\n\n\n\n\n\n\n\n$
is is. \nis? this \nis \n\n\n\n\n\n\n\n\n\n\n$
is is. is? this \nis \n\n\n\n\n\n\n\n\n\n\n\n$
is is. is? this us
is is. is? this is