Execute um subshell bash interativo com comandos iniciais sem retornar ao shell (“super”) imediatamente

64

Eu quero rodar um subshell bash, (1) executar alguns comandos, (2) e então permanecer nessa subshell para fazer o que eu quiser. Eu posso fazer cada um deles individualmente:

  1. Executar comando usando -c flag:

    $> bash -c "ls; pwd; <other commands...>"
    

    no entanto, ele imediatamente retorna ao shell "super" depois que os comandos são executados. Eu também posso apenas executar um subshell interativo:

  2. Iniciar novo bash process:

    $> bash
    

    e ele não sairá da subshell até que eu diga isso explicitamente ... mas não posso executar nenhum comando inicial. A solução mais próxima que encontrei é:

    $> bash -c "ls; pwd; <other commands>; exec bash"
    

    que funciona, mas não do jeito que eu queria, pois ele executa os comandos fornecidos em uma sub-camada e abre um outro separado para interação.

Eu quero fazer isso em uma única linha. Uma vez que eu saio do subshell, eu devo retornar ao shell "super" regular sem incidentes. Deve haver um jeito ~~

NB: O que eu não estou perguntando ...

  1. não perguntando onde conseguir a página do bash man
  2. não está perguntando como ler comandos de inicialização a partir de um arquivo ... Eu sei como fazer isso, não é a solução que estou procurando
  3. não está interessado em usar a tela do tmux ou do gnu
  4. não está interessado em dar contexto a isso. Ou seja, a questão deve ser geral e não para qualquer propósito específico
  5. Se possível, eu quero evitar usar soluções alternativas que realizem o que eu quero, mas de uma maneira "suja". Eu só quero fazer isso em uma única linha. Em particular, não quero fazer algo como xterm -e 'ls'
por SABBATINI Luca 09.03.2012 / 16:12

7 respostas

60

Isso pode ser feito facilmente com pipes nomeados temporários :

bash --init-file <(echo "ls; pwd")

O crédito por esta resposta vai para o comentário de Lie Ryan . Achei isso muito útil e é menos perceptível nos comentários, então achei que deveria ser sua própria resposta.

    
por 02.04.2014 / 22:17
9

Você pode fazer isso de maneira indireta com um arquivo temporário, embora seja necessário duas linhas:

echo "ls; pwd" > initfile
bash --init-file initfile
    
por 09.03.2012 / 17:21
3

Tente isso:

$> bash -c "ls;pwd;other commands;$SHELL"

$SHELL Torna o shell aberto no modo interativo, aguardando um fechamento com exit .

    
por 23.02.2017 / 04:50
2

A "solução Esperar" a que me refiro é a programação de um shell bash com a Esperar linguagem de programação :

#!/usr/bin/env expect
set init_commands [lindex $argv 0]
set bash_prompt {\$ $}              ;# adjust to suit your own prompt
spawn bash
expect -re $bash_prompt {send -- "$init_commands\r"}
interact
puts "exiting subshell"

Você executaria isso como: ./subshell.exp "ls; pwd"

    
por 09.03.2012 / 20:56
1

Se sudo -E bash não funcionar, usei o seguinte, que atendeu às minhas expectativas até o momento:

sudo HOME=$HOME bash --rcfile $HOME/.bashrc

Eu defino HOME = $ HOME porque quero que minha nova sessão tenha o HOME definido para o HOME do meu usuário, em vez do HOME do root, que acontece por padrão em alguns sistemas.

    
por 16.09.2017 / 17:07
0

menos elegante que --init-file , mas talvez mais fácil de usar:

fn(){
    echo 'hello from exported function'
}

while read -a commands
do
    eval ${commands[@]}
done
    
por 17.04.2015 / 03:37
0

Por que não usar subpastas nativas?

$ ( ls; pwd; exec $BASH; )
bar     foo     howdy
/tmp/hello/
bash-4.4$ 
bash-4.4$ exit
$

Colocar comandos com parênteses faz com que o bash crie um subprocesso para executar esses comandos, para que você possa, por exemplo, alterar o ambiente sem afetar o shell pai. Isso é basicamente mais legível equivalente ao bash -c "ls; pwd; exec $BASH" .

Se isso ainda parecer detalhado, existem duas opções. Uma é ter esse trecho como uma função:

$ run() { ( eval "$@"; exec $BASH; ) }
$ run 'ls; pwd;'
bar     foo     howdy
/tmp/hello/
bash-4.4$ exit
$ run 'ls;' 'pwd;'
bar     foo     howdy
/tmp/hello/
bash-4.4$ exit
$

Outra é tornar exec $BASH mais curto:

$ R() { exec $BASH; }
$ ( ls; pwd; R )
bar     foo     howdy
/tmp/hello/
bash-4.4$ exit
$

Eu pessoalmente gosto de R se aproximar mais, já que não há necessidade de tocar com strings de escape.

    
por 14.08.2017 / 23:51