¿Por qué no puedo abrir un shell desde un process en línea?

He condensado mi problema en el siguiente código:

#include <stdio.h> int main(){ char buffer[256]; printf("Enter input: "); scanf("%s", buffer); system("/bin/sh"); return 0; } 

Si ejecuto este progtwig con la input del usuario, obtengo:

 user@ubuntu:~/testing/temp$ ./main Enter input: Test $ 

Con la última línea el shell que el progtwig comenzó.

Pero si ejecuto el progtwig con la input proveniente de la tubería:

 user@ubuntu:~/testing/temp$ echo 'test' | ./main Enter input: user@ubuntu:~/testing/temp$ 

El progtwig no parece abrir el shell.


Después de algunos retoques, me di count de que si hacía esto:

 user@ubuntu:~/testing/temp$ (python -c "print 'test'" ; echo 'ls') | ./main a.out main main.c Enter input: user@ubuntu:~/testing/temp 

Pude ejecutar el command ls en el shell abierto.

Entonces, dos preguntas:

  1. ¿Por qué el caparazón no se abre como en el primer caso?
  2. ¿Cómo puedo lidiar con esto? Es muy inconveniente tener que decidir qué commands ejecutar antes de ejecutar el progtwig: prefiero tener un shell donde pueda elegir dinámicamente qué commands ejecutar.

  1. ¿Por qué el caparazón no se abre como en el primer caso?

En el primer caso, stdin es un terminal y el shell es interactivo. Espera tus commands, etc.

En el segundo caso, stdin es un conducto y el shell no es interactivo. Su progtwig consume la primera línea en stdin (es decir, la test\n cadena test\n ), luego el shell intenta leer stdin y ve EOF . Sale, porque eso es lo que se supone que hacen los progtwigs que reciben EOF en la input.

En el tercer caso, el shell nuevamente no es interactivo, por el mismo motivo. Su scanf() consume la primera línea en stdin (es decir, test\n ), luego el shell lee ls . El shell ejecuta ls , intenta leer más commands, ve EOF y sale.

  1. ¿Cómo puedo lidiar con esto?

Si al "tratar con eso" te refieres a ejecutar un shell interactivo cuando stdin está conectado a un conducto, la solución es usar un pty(7) .

Su progtwig C se comportará de forma muy similar a (read x; /bin/sh) .

Si simplemente escribe esto recortado en una línea de command, entonces su input estándar está conectada al keyboard de su terminal; se leerá una línea, y luego sh leerá más líneas hasta que ocurra una condición de fin de file (por lo general, se puede presionar Ctrl-D para causar una).

En el ejemplo donde canaliza en su progtwig, el tamaño de input está limitado; es lo que pones allí y nada más: las líneas después de la primera, si hay alguna, serán interpretadas por sh , que luego saldrá.

Podría simular la caja sin tubería usando cat para reenviar su input de keyboard a sh :

 (echo test; cat) | (read x; /bin/sh) 

o quizás:

 (echo test; cat) | ./main 

Sin embargo, eso probablemente no sea tan bueno como ejecutar un shell directamente; detectando que su input no es un terminal, probablemente deshabilitará sus capacidades de edición de línea.

Para ejecutar un shell interactivo conectado a un conducto, solo tiene que hacerlo interactivo.

 input | /bin/sh -i 

El shell no necesita un terminal para ser interactivo, necesita una input interactiva. En general, el comportamiento del shell cuando se ejecuta de forma interactiva tiene muy poco que ver con los terminales (al less según las especificaciones ) y generalmente difiere del comportamiento de un shell no interactivo donde el event handling errores se trata más que cualquier otra cosa. Los shells interactivos tienden a no salir en condiciones de error que de lo contrario harían que un shell no interactivo se cierre. Sin embargo, un intérprete de commands debería usar el modo interactivo de forma pnetworkingeterminada si la input proviene de un terminal.

Algunos shells pueden requerir input de terminal para uso interactivo, pero esto es una ilusión. De hecho, estos shells generalmente funcionan muy parecidos a su caso de ejemplo: bash , por ejemplo, configura readline para manejar el i / o del terminal nitty-gritty en su nombre, zsh invoca su editor de línea ZLE y dash (si no comstackdo con los enlaces SMALL de la opción de time de compilation) en la biblioteca libedit de BSD. Estos editores de línea leen y procesan la input del terminal en algo así como un script de shell línea por línea, que luego el shell ejecuta según corresponda.

Sin embargo, no estás teniendo problemas con ninguno de esos editores. A juzgar por su request y su llamada ejecutiva, está llamando a un dash que se comstack con la opción de time de compilation SMALL (el valor pnetworkingeterminado de Debian) en cuyo caso solo funcionará, pero las únicas funciones de edición de línea que puede get de ellos serán los proporcionados nativamente por la línea de disciplina de la terminal (ver stty ) .

Su problema es que el shell no tiene ninguna input: cuando detecta EOF, muere, como ya se ha observado en otro lugar. Usted podría hacer …

 input | /bin/sh -i -o ignoreeof 

Pero probablemente no te gusten los resultados. dash no dejará de fumar en la décima lectura nula consecutiva que algunos proyectiles hacen, simplemente se imprimirá …

 Type 'exit' to exit the shell 

… a stderr para siempre Un poco mejor podría ser …

 cat input - | /bin/sh -i 

… para concatelizar los commands de shell en el file de input con cat 's - stdin a su stdout y luego ejecutar los resultados en un interactivo /bin/sh . Esto funcionará, aunque es posible que desee asegurarse de configurar la stty línea stty en algo así como un estado canónico y con una tecla de erase apropiada para que al less pueda get un retroceso funcional. Probablemente ya esté configurado correctamente, pero vale la pena verificarlo de todos modos.

De otra manera…

 echo ": some command; exec <$(tty)" | /bin/sh -i 

Los methods anteriores funcionarán porque su canalización es el trabajo de primer plano actual en el terminal de control: su canal posee actualmente la input de terminal y cat está leyéndola. Esto se contrasta con python -c y echo que no lo leen, sino que solo transfieren la salida generada por los arguments de command-line, por lo que cuando termina su salida también lo hace la input de su shell. Esto es cierto a less que se instruya al shell para que busque la input en otro lugar como lo hago con echo en el segundo ejemplo.

El terminal es solo una posible fuente de input de muchos a un shell, y se puede manejar de muchas forms diferentes. El líder de la session de su terminal, parece que bash , espera a que finalice el control de la terminal de su tubería para que pueda recuperar el control y hacer lo siguiente. Esta información se le pasará como una señal asíncrona: para eso están los terminales. Los terminales multiplexan un par de pantalla / input en cualquier cantidad de processs de lectura / printing. Lo hacen muy bien.

Por ejemplo:

 $ PS1='bgsh1: ' sh -i +m & PS1='bgsh2: ' sh -i +m & $ bgsh1: bgsh2: [2] + Stopped (tty input) sh -i +m [1] - Stopped (tty input) sh -i +m $ i=0; while [ "$((i+=1))" -lt 5 ]; do fg "%$(((i%2)+1))"; done sh -i +m bgsh2: var=something bgsh2: kill -TSTP $$ sh -i +m bgsh1: var=else bgsh1: kill -TSTP $$ sh -i +m bgsh2: echo $var; kill -TSTP $$ something sh -i +m bgsh1: echo $var; kill -TSTP $$ else [1] + Stopped sh -i +m [2] - Stopped sh -i +m $