Para executar um shell interativo conectado a um pipe, você só precisa torná-lo interativo.
input | /bin/sh -i
O shell não precisa de um terminal para ser interativo - ele precisa de entrada interativa. Na maior parte, o comportamento do shell quando executado interativamente tem muito pouco a ver com terminais - (pelo menos de acordo com spec ) - e geralmente difere do comportamento de um shell não interativo em relação ao tratamento de erros, mais do que qualquer outra coisa. Os shells interativos tendem a não sair em condições de erro que, de outra forma, causariam o encerramento de um shell não interativo. Um shell deve padrão para o modo interativo, se a entrada vier de um terminal.
Algumas camadas parecem requerer entrada de terminal para uso interativo, mas isso é uma ilusão. Na verdade, esses shells normalmente funcionam muito parecido com o seu caso de exemplo sob o capô - bash
, por exemplo, configura readline
para manipular o terminal i / o nitty-gritty em seu nome, zsh
invoca sua linha ZLE editor, e dash
(se não compilado com a opção de tempo de criação SMALL ) links na biblioteca libedit
do BSD. Esses editores de linha leem e processam a entrada do terminal em algo parecido com um shell script linha por linha, que o shell então executa conforme apropriado.
Você não está tendo problemas com nenhum desses editores. A julgar pelo seu prompt e sua chamada execve, você está chamando um dash
que é compilado com a opção em tempo de criação SMALL (o padrão da Debian) nesse caso ele apenas funcionará - mas as únicas funções de edição de linha que você pode obter são aquelas fornecidas nativamente pela disciplina de linha do terminal (veja stty
) .
Seu problema é que o shell não possui nenhuma entrada - quando ele detecta EOF, ele morre como já foi observado em outro lugar. Você pode fazer ...
input | /bin/sh -i -o ignoreeof
Mas você provavelmente não gostará dos resultados. dash
não fará o quit na décima coisa consecutiva de leitura nula que alguns shells fazem - apenas imprimirá ...
Type 'exit' to exit the shell
... para stderr para sempre . Um pouco melhor poderia ser ...
cat input - | /bin/sh -i
... para concatenar os comandos do shell no arquivo input
com cat
' -
stdin para seu stdout e, em seguida, para executar os resultados em um /bin/sh
interativo. Isso funcionará - embora você queira garantir a configuração da stty
line-discipline para algo como um estado canônico e com uma chave erase
apropriada para que você possa pelo menos obter um backspace funcional. Isso provavelmente já está definido corretamente - mas vale a pena verificar de qualquer maneira.
Outra maneira ...
echo ": some command; exec <$(tty)" | /bin/sh -i
Os métodos acima funcionarão porque o pipeline é o trabalho atual em primeiro plano no terminal de controle - seu pipeline atualmente possui a entrada do terminal e cat
está realmente lendo. Isso é contrastado por python -c
e echo
, que não o lêem - eles só transmitem a saída como gerada pelos argumentos da linha de comando - e, assim, quando a saída termina, também ocorre a entrada do seu shell. Isso é verdade a menos que o shell seja instruído a procurar informações em outro lugar, como acontece com echo
no segundo exemplo.
O terminal é apenas uma fonte possível de entrada de muitos para um shell - e pode ser tratado de muitas maneiras diferentes. O líder de sessão do seu terminal - parece que bash
- aguarda o término do controle de terminal do seu pipeline para que ele possa recuperar o controle e fazer a próxima tarefa. Esta informação será passada para ela como um sinal assíncrono - é para isso que servem os terminais. Os terminais multiplexam um par de exibição / entrada em qualquer número de processos de leitura / impressão. Eles fazem isso muito bem.
Por exemplo:
$ 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
$