ksh93
ma dyscypliny, które są zwykle używane do tego rodzaju rzeczy. Dzięki zsh
, można porwać katalogu dynamiczny o nazwie funkcji :
Zdefiniuj na przykład:
zsh_directory_name() {
case $1 in
(n)
case $2 in
(incr) reply=($((++incr)))
esac
esac
}
A następnie możesz użyć, ~[incr]
aby uzyskać przyrost za $incr
każdym razem:
$ echo ~[incr]
1
$ echo ~[incr] ~[incr]
2 3
Twoje podejście kończy się niepowodzeniem, ponieważ w head -1 /tmp/ints
, głowa otwiera fifo, czyta pełny bufor, drukuje jedną linię, a następnie ją zamyka . Po zamknięciu koniec pisania widzi zepsutą rurkę.
Zamiast tego możesz albo:
$ fifo=~/.generators/incr
$ (umask 077 && mkdir -p $fifo:h && rm -f $fifo && mkfifo $fifo)
$ seq infinity > $fifo &
$ exec 3< $fifo
$ IFS= read -rneu3
1
$ IFS= read -rneu3
2
Tam zostawiamy otwarty koniec odczytu na fd 3 i read
odczytujemy jeden bajt na raz, a nie pełny bufor, aby upewnić się, że przeczytano dokładnie jedną linię (do znaku nowej linii).
Lub możesz zrobić:
$ fifo=~/.generators/incr
$ (umask 077 && mkdir -p $fifo:h && rm -f $fifo && mkfifo $fifo)
$ while true; do echo $((++incr)) > $fifo; done &
$ cat $fifo
1
$ cat $fifo
2
Tym razem tworzymy instancję potoku dla każdej wartości. Umożliwia to zwracanie danych zawierających dowolną liczbę wierszy.
Jednak w takim przypadku, gdy tylko cat
otwiera się fifo, echo
pętla i jest odblokowywana, aby echo
można było uruchomić więcej, zanim cat
odczyta zawartość i zamknie potok (powodując, że następny echo
utworzy nowy potok).
Rozwiązaniem może być dodanie opóźnienia, na przykład poprzez uruchomienie zewnętrznego, echo
jak sugeruje @jimmij, lub dodanie go sleep
, ale nadal nie byłoby to zbyt solidne, lub można odtworzyć nazwaną potok po każdym echo
:
while
mkfifo $fifo &&
echo $((++incr)) > $fifo &&
rm -f $fifo
do : nothing
done &
Że nadal pozostawia krótkie okna gdzie nie istnieją rura (między unlink()
zrobione przez rm
i mknod()
wykonywane przez mkfifo
) spowodowanie cat
, by upaść, i bardzo krótkich okna gdzie rura została instancja ale nie proces będzie kiedykolwiek ponownie napisać do niego (między write()
i close()
zrobione przez echo
) powodując, cat
że nic nie zwróci, i krótkie okna, w których nazwany potok nadal istnieje, ale nic nigdy go nie otworzy do zapisu (między close()
wykonanym przez echo
a unlink()
wykonanym przez rm
) gdzie cat
zawiesi się.
Możesz usunąć niektóre z tych okien , wykonując następujące czynności:
fifo=~/.generators/incr
(
umask 077
mkdir -p $fifo:h && rm -f $fifo && mkfifo $fifo &&
while
mkfifo $fifo.new &&
{
mv $fifo.new $fifo &&
echo $((++incr))
} > $fifo
do : nothing
done
) &
W ten sposób jedynym problemem jest uruchomienie kilku kotów jednocześnie (wszystkie otwierają fifo, zanim nasza pętla zapisu będzie gotowa do otwarcia go do zapisu), w którym to przypadku będą dzielić dane echo
wyjściowe.
Odradzałbym także tworzenie stałych nazw, czytelnych na całym świecie fifos (lub jakichkolwiek innych plików w tym zakresie) w katalogach zapisywalnych na świecie, takich jak /tmp
chyba, że jest to usługa dostępna dla wszystkich użytkowników w systemie.
command echo
lub/bin/echo
zamiast wbudowanegoecho
. Także - można zrobić to polecenie trochę krótsza:repeat 999 /bin/echo $((++incr)) > /tmp/int &
.Jeśli chcesz wykonać kod za każdym razem, gdy odczytywana jest wartość zmiennej, nie możesz tego zrobić w samym zsh.
RANDOM
Zmienna (jak innych podobnych zmiennych specjalnych) jest zakodowane w kodzie źródłowym zsh. Możesz jednak zdefiniować podobne zmienne specjalne, pisząc moduł w C. Wiele standardowych modułów definiuje zmienne specjalne.Możesz użyć koprocesu, aby utworzyć generator.
Jest to jednak dość ograniczone, ponieważ możesz mieć tylko jeden koproces. Innym sposobem na stopniowe uzyskiwanie danych wyjściowych z procesu jest przekierowanie z substytucji procesu .
Zauważ, że
head -1
tutaj nie działa, ponieważ czyta cały bufor, drukuje to, co lubi i wychodzi. Dane odczytane z potoku pozostają odczytane; jest to nieodłączna właściwość potoków (nie można ponownie wprowadzać danych).read
Wbudowane unika tego problemu, czytając jeden bajt na raz, co pozwala na to, aby zatrzymać jak tylko znajdzie pierwszy znak nowej linii, ale jest bardzo powolny (oczywiście, że nie ma znaczenia, czy jesteś po prostu czytanie kilkaset bajtów).źródło
bash
, patrz sekcja bash pod tym linkiem.coproc
koprocesy, to znaczy nie zpty)coproc cmd1; exec 3>&p 4<&p; coproc cmd2 3>&- 4<&-...
Myślę, że zrobiłbym to z jakimś sygnałem.
W każdym razie to dla mnie działa.
Z niewielkiej uwagi, oto coś dziwnego, co odkryłem poprzedniego dnia:
Robi się też dziwniej:
źródło
bash
jest zachowanie się zmieniło? Myślę, że stwierdzenie opwd
nie sprawdzaniu i odwoływaniu się tylko do$PWD
jest niepoprawne.mkdir /tmp/dir; cd $_; PS4='$OLDPWD, $PWD + '; set -x; OLDPWD=$OLDPWD PWD=$PWD command eval ' cd ..; cd ..; cd ~; pwd'; pwd; cd .; pwd
może pokazać ci, co mam na myśli. To problem, który mnie zepsułns()
.