Problema general
Quiero escribir un script que interactúe con el usuario a pesar de que está en medio de una cadena de tuberías.
Ejemplo concreto
Concretamente, toma una file
o stdin
, muestra líneas (con números de línea), le pide al usuario que ingrese una selección o números de línea, y luego imprime las líneas correspondientes en stdout
. Llamemos a este script selector
. Entonces, básicamente, quiero poder hacer
grep abc foo | selector > myfile.tmp
Si foo
contiene
blabcbla
foo abc bar
quux
xyzzy abc
luego selector
me presenta (¡en la terminal, no dentro myfile.tmp
!) con opciones
1) blabcbla
2) foo abc bar
3) xyzzy abc
Select options:
después de lo cual escribo
2-3
y terminar con
foo abc bar
xyzzy abc
como contenido de myfile.tmp
.
Tengo un script selector en funcionamiento, y básicamente funciona perfectamente si no redirijo la entrada y la salida. Entonces
selector foo
se comporta como yo quiero. Sin embargo, al unir elementos como en el ejemplo anterior, selector
imprime las opciones presentadas myfile.tmp
e intenta leer una selección de la entrada grepped.
Mi acercamiento
He tratado de usar la -u
bandera de read
, como en
exec 4< /proc/$PPID/fd/0
exec 4> /proc/$PPID/fd/1
nl $INPUT >4
read -u4 -p"Select options: "
Pero esto no hace lo que esperaba.
P: ¿Cómo obtengo la interacción real del usuario?
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; }'
que funciona bastante bien. Sin embargo segrep b foo | selector | wc -l
rompe por aquí. ¿Hay alguna idea de cómo arreglar eso? Por cierto, elrangeselect
que usé se puede encontrar en pastebin.com/VAxTSSHs . Es un script AWK simple que imprime las líneas de un archivo correspondiente a un rango dado de números de lino. (Los rangos pueden ser cosas como "3-10, 12,14,16-20".)alias
eso, más bienselector() { all of that stuff...; }
en una función.alias
es renombrar comandos simples mientras que las funciones empaquetan un comando compuesto en un solo comando simple .Respuestas:
El uso
/proc/$PPID/fd/0
no es confiable: el padre delselector
proceso puede no tener el terminal como entrada.Hay una ruta estándar que siempre se refiere a la terminal del proceso actual:
/dev/tty
.o
fuente
He escrito una pequeña función: no responderá sobre lo que ha pedido encadenamiento de tubería, pero resolverá su problema.
La función entrega todos los argumentos a los que se los da de inmediato
grep
. Si usa un glob de shell para especificar los archivos que debería leer, devolverá todas las coincidencias en todos los archivos, comenzando con el primero en el orden glob y terminando con la última coincidencia.grep
pasa su salida anl
qué números cada línea y cuál pasa su salida a latee
cual duplica su salida tanto astdout
como a/dev/tty
. Esto significa que la salida de la tubería se imprime simultáneamente tanto en la matriz de argumentos de la función donde se divide en\n
líneas electrónicas como en el terminal a medida que funciona.Luego, la
_in()
función intentaread
en una selección si hay al menos 1 resultado de la acción anterior un máximo de cinco veces. La selección puede consistir solo en números separados por espacios, o bien rangos de números separados por-
. Si hay algo másread
(incluida una línea en blanco) volverá a intentarlo, pero solo, como antes, un máximo de cinco veces.Por último, la
_out()
función analiza la selección del usuario y expande cualquier rango en ella. Imprime sus resultados en el formulario"${[num]}"
para cada uno, igualando así el valor de las líneas almacenadas eninf()
la matriz arg de. Esta salida seeval
edita como argumentos en losprintf
que, por lo tanto, imprime solo las líneas que el usuario ha seleccionado.Es explícitamente
read
desde la terminal y solo imprime elSelect:
menústderr
y, por lo tanto, es mucho más fácil de usar. Por ejemplo, lo siguiente funciona:Pero puede usar cualquier opción que le dé
grep
y cualquier cantidad de nombres de archivo que pueda darle también. Es decir, puede usar cualquier tipo, ya que como efecto secundario de su entrada de análisis$IFS
no funcionará si está buscando líneas en blanco. Pero, ¿quién querría seleccionar de una lista numerada de líneas en blanco?Última nota que debido a que esto funciona traduciendo directamente la entrada numérica del usuario en los parámetros posicionales numéricos almacenados en la matriz de argumentos de la función, la salida será lo que el usuario seleccione, tantas veces como el usuario lo seleccione, y en el orden que el usuario seleccione eso.
Por ejemplo:
fuente