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ęść sed
po prostu przenosi liczbę is
wystąpień z jednej linii do drugiej. Powinien niezawodnie obsłużyć tyle is
esó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 is
napotkanego, 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, is
wstawiając \n
ewline, zanim wszystkie jego wystąpienia is
poprzedzą zero lub jeden znak interpunkcyjny, a po nim spację. Robi kolejne przejście i usuwa wszystkie \n
owline, które są bezpośrednio poprzedzone znakiem spacji. Te pozostawione znaczniki będą pasować is.
, a is
jednak nie this
lub ?is
.
Następnie zbiera każdy znacznik na ogonie sznurka - za każde \ni
dopasowanie w linii dołącza \n
ewlinię do ogona sznurka i zastępuje go jednym i
lub u
. Jeśli są 3 \n
ewline 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 l
polecenie 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ą is
es 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