Ogólny problem
Chcę napisać skrypt, który wchodzi w interakcję z użytkownikiem, nawet jeśli jest on w środku łańcucha potoków.
Konkretny przykład
Konkretnie, zajmuje to file
lub stdin
wyświetla linie (z numerami linii), prosi użytkownika o wprowadzenie wyboru lub numerów linii, a następnie drukuje odpowiednie linie do stdout
. Nazwijmy ten skrypt selector
. Więc w zasadzie chcę być w stanie to zrobić
grep abc foo | selector > myfile.tmp
Jeśli foo
zawiera
blabcbla
foo abc bar
quux
xyzzy abc
następnie selector
przedstawia mi (na terminalu, nie w myfile.tmp
!) opcje
1) blabcbla
2) foo abc bar
3) xyzzy abc
Select options:
po czym piszę
2-3
i skończyć z
foo abc bar
xyzzy abc
jako zawartość myfile.tmp
.
Mam gotowy skrypt selektora i zasadniczo działa idealnie, jeśli nie przekierowuję danych wejściowych i wyjściowych. Więc
selector foo
zachowuje się tak, jak chcę. Jednak podczas przesyłania strumieniowego razem, jak w powyższym przykładzie, selector
drukuje przedstawione opcje myfile.tmp
i próbuje odczytać zaznaczenie z grepped input.
Moje podejście
Próbowałem użyć -u
flagi read
, jak w
exec 4< /proc/$PPID/fd/0
exec 4> /proc/$PPID/fd/1
nl $INPUT >4
read -u4 -p"Select options: "
ale to nie robi tego, na co liczyłem.
P: Jak uzyskać faktyczną interakcję użytkownika?
cmd | { some processing; read var </dev/tty; } | cmd
alias selector='{ TMPFILE=$(mktemp); cat > $TMPFILE; nl -s") " $TMPFILE | column -c $(tput cols); read -e -p"Select options: " < /dev/tty; rangeselect -v range="$REPLY" $TMPFILE; rm $TMPFILE; }'
który działa całkiem dobrze. Jednakgrep b foo | selector | wc -l
tutaj się psuje. Wszelkie pomysły, jak to naprawić? Nawiasem mówiąc, torangeselect
, którego użyłem, można znaleźć na pastebin.com/VAxTSSHs . Jest to prosty skrypt AWK, który drukuje wiersze pliku odpowiadające danemu zakresowi numerów bielizny. (Zakresy mogą być takie jak „3-10, 12,14,16-20”.)alias
tego raczejselector() { all of that stuff...; }
w funkcji.alias
es zmienia nazwy prostych poleceń, podczas gdy funkcje pakują złożone polecenie w jedno proste polecenie .Odpowiedzi:
Używanie
/proc/$PPID/fd/0
jest zawodne: rodzicselector
procesu może nie mieć terminala jako danych wejściowych.Jest to standardowy ścieżka że zawsze odnosi się do terminala bieżącego procesu:
/dev/tty
.lub
źródło
Napisałem małą funkcję: nie odpowie na to, o co prosiłeś o łączenie rur, ale rozwiąże twój problem.
Funkcja odwraca wszystkie argumenty, którym ją natychmiast przekazujesz
grep
. Jeśli użyjesz globu powłoki, aby określić pliki, które powinien z niego odczytać, zwróci wszystkie dopasowania we wszystkich plikach, zaczynając od pierwszego w kolejności globu, a kończąc na ostatnim dopasowaniu.grep
przekazuje dane wyjściowe, donl
których numeruje każdą linię i które przekazuje dane wyjściowe, dotee
których duplikuje dane wyjściowe zarówno do, jakstdout
i do/dev/tty
. Oznacza to, że dane wyjściowe z potoku są jednocześnie drukowane zarówno w tablicy argumentów funkcji, gdzie są dzielone na\n
ewline, jak i na terminalu, gdy działa.Następnie
_in()
funkcja próbuje dokonaćread
selekcji, jeśli jest co najmniej 1 wynik z poprzedniej akcji maksymalnie pięć razy. Wybór może składać się tylko z liczb oddzielonych spacjami lub zakresów liczb oddzielonych-
. Jeśli cokolwiek innego jestread
(w tym pusta linia) , spróbuje ponownie - ale tylko, jak poprzednio, maksymalnie pięć razy.Na koniec
_out()
funkcja analizuje wybór użytkownika i rozszerza zawarte w nim zakresy."${[num]}"
Dla każdego wypisuje swoje wyniki w formie - dopasowując w ten sposób wartość linii przechowywanych winf()
tablicy arg. Dane wyjściowe sąeval
edytowane jako argumenty, wprintf
związku z czym wypisuje tylko linie wybrane przez użytkownika.Wyraźnie
read
pochodzi z terminala i drukuje tylkoSelect:
menu,stderr
dzięki czemu jest przyjazny dla potoków. Na przykład następujące prace:Możesz jednak użyć dowolnych opcji
grep
i dowolnej liczby nazw plików, które możesz mu podać . Oznacza to, że możesz użyć dowolnego innego niż jeden - ponieważ efekt uboczny jego analizy składniowej$IFS
nie będzie działał, jeśli szukasz pustych linii. Ale kto chciałby wybrać z ponumerowanej listy pustych linii?Ostatnia uwaga, ponieważ ponieważ działa to poprzez bezpośrednie przetłumaczenie numerycznych danych wejściowych użytkownika na numeryczne parametry pozycyjne zapisane w tablicy argumentów funkcji, wynik będzie taki, jak wybierze użytkownik, tyle razy, ile wybierze użytkownik, i w dowolnej kolejności, którą wybierze użytkownik to.
Na przykład:
źródło