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'
Ta część sedpo prostu przenosi liczbę iswystąpień z jednej linii do drugiej. Powinien niezawodnie obsłużyć tyle isesów w linii, ile w niego rzucisz, i nie musi buforować starych linii, podczas gdy to robi - zachowuje tylko jeden znak nowej linii dla każdego isnapotkanego, który nie jest częścią innego słowa.
Rezultatem jest to, że zmodyfikuje tylko trzecie wystąpienie w pliku - i będzie przenosił liczbę na linię. Więc jeśli plik wygląda jak:
1. is is isis
2. is does
... wydrukuje ...
1. is is isis
2. us does
Najpierw obsługuje skrzynki na krawędziach, wstawiając odstęp na czubku i na końcu każdej linii. To sprawia, że granice słów są nieco łatwiejsze do ustalenia.
Następnie szuka poprawnych znaków, iswstawiając \newline, zanim wszystkie jego wystąpienia ispoprzedzą zero lub jeden znak interpunkcyjny, a po nim spację. Robi kolejne przejście i usuwa wszystkie \nowline, które są bezpośrednio poprzedzone znakiem spacji. Te pozostawione znaczniki będą pasować is., a isjednak nie thislub ?is.
Następnie zbiera każdy znacznik na ogonie sznurka - za każde \nidopasowanie w linii dołącza \newlinię do ogona sznurka i zastępuje go jednym ilub u. Jeśli są 3 \newline w rzędzie zebrane na końcu sznurka, wówczas używa u - w przeciwnym razie i. Pierwsze użycie au jest również ostatnie - zamiennik uruchamia nieskończoną pętlę, która sprowadza się do get line, print line, get line, print line,i tak dalej.
Pod koniec każdego cyklu pętli próbnej czyści wstawione spacje, drukuje tylko do pierwszego występującego nowego wiersza w przestrzeni wzorów i wraca.
Dodam lpolecenie ook na początku pętli, takie jak:
l; s/\ni(.* )\n{9}/u\1/...
... i zobacz, jak to działa, gdy działa z tymi danymi wejściowymi:
hai this is linux.
hai this is unix.
hai this is mac.
hai this is unchanged is.
... więc oto, co robi:
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
Ma to większy sens, może z większą liczbą ises na linię:
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'
)
To praktycznie to samo, ale napisane w / POSIX BRE i podstawowa obsługa argumentów.
printf 'is is. is? this is%.0s\n' {1..4} | nthword is us 12
... dostaje ...
is is. is? this is
is is. is? this is
is is. is? this us
is is. is? this is
... a jeśli włączę ${dbg}:
printf 'is is. is? this is%.0s\n' {1..4} |
dbg=1 nthword is us 12
... możemy obserwować iterację ...
\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