web-dev-qa-db-fra.com

Exécutez un sous-shell bash interactif avec les commandes initiales sans retourner immédiatement au ("super") Shell

Je veux exécuter un sous-shell bash, (1) exécuter quelques commandes, (2) puis rester dans ce sous-shell pour faire ce que je veux. Je peux faire chacun de ces éléments individuellement:

  1. Exécutez la commande à l'aide de -c drapeau:

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

    cependant, il revient immédiatement au "super" Shell après l'exécution des commandes. Je peux aussi simplement lancer un sous-shell interactif:

  2. Démarrer un nouveau processus bash:

    $> bash
    

    et il ne quittera pas le sous-shell jusqu'à ce que je le dise explicitement ... mais je ne peux exécuter aucune commande initiale. La solution la plus proche que j'ai trouvée est:

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

    qui fonctionne, mais pas comme je le voulais, car il exécute les commandes données dans un sous-shell, puis en ouvre une autre pour l'interaction.

Je veux le faire sur une seule ligne. Une fois que j'ai quitté le sous-shell, je devrais retourner au Shell "super" normal sans incident. Il doit y avoir un moyen ~~

NB: Ce que je ne demande pas ...

  1. ne pas demander où se procurer la page de manuel bash
  2. ne me demande pas comment lire les commandes d'initialisation d'un fichier ... je sais comment faire, ce n'est pas la solution que je cherche
  3. pas intéressé à utiliser l'écran tmux ou gnu
  4. pas intéressé à donner un contexte à cela. C'est-à-dire que la question est censée être générale et non à des fins spécifiques
  5. si possible, je veux éviter d'utiliser des solutions de contournement qui accomplissent ce que je veux, mais d'une manière "sale". Je veux juste le faire sur une seule ligne. En particulier, je ne veux pas faire quelque chose comme xterm -e 'ls'
96
SABBATINI Luca

Cela peut être facilement fait avec canaux nommés temporaires :

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

Le mérite de cette réponse revient au commentaire de Lie Ryan . J'ai trouvé cela vraiment utile, et c'est moins perceptible dans les commentaires, alors j'ai pensé que ce devrait être sa propre réponse.

85
Jonathan Potter

Vous pouvez le faire de manière détournée avec un fichier temporaire, même si cela prendra deux lignes:

echo "ls; pwd" > initfile
bash --init-file initfile
12
Eduardo Ivanec

Essayez plutôt ceci:

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

$Shell Il ouvre le Shell en mode interactif, en attendant sa fermeture avec exit.

6
ExpoBi

La "solution attendue" à laquelle je faisais référence était la programmation d'un shell bash avec le langage de programmation Expect :

#!/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"

Vous exécuteriez cela comme: ./subshell.exp "ls; pwd"

3
glenn jackman

Pourquoi ne pas utiliser des sous-coquilles natives?

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

Le fait de placer des commandes entre parenthèses fait de bash spawn un sous-processus pour exécuter ces commandes, vous pouvez donc, par exemple, modifier l'environnement sans affecter le shell parent. C'est essentiellement l'équivalent plus lisible que le bash -c "ls; pwd; exec $BASH".

Si cela semble encore verbeux, il y a deux options. L'une consiste à avoir cet extrait de code en fonction:

$ 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
$

Une autre consiste à faire exec $BASH plus court:

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

Personnellement, j'aime davantage l'approche R, car il n'est pas nécessaire de jouer avec des chaînes d'échappement.

1
toriningen

Si Sudo -E bash ne fonctionne pas, j'utilise ce qui suit, ce qui a répondu à mes attentes jusqu'à présent:

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

J'ai défini HOME = $ HOME parce que je veux que ma nouvelle session ait HOME défini sur HOME de mon utilisateur, plutôt que sur HOME de root, ce qui se produit par défaut sur certains systèmes.

1
jago

moins élégant que --init-file, mais peut-être plus instrumentable:

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

while read -a commands
do
    eval ${commands[@]}
done
0
RubyTuesdayDONO

J'accomplis essentiellement la même chose en utilisant simplement un script, généralement dans le but de définir des variables d'environnement à utiliser dans un répertoire de projet spécifique;

$ cat Shell.sh
#!/bin/bash
export PATH=$PWD/bin:$PATH
export USERNAME=foo
export PASSWORD=bar
export DB_SERVER=http://localhost:6001
bash

$ echo ${USERNAME:-none}
none

$ ./Shell.sh

$ echo $USERNAME
foo

Cela vous dépose dans un shell interactif bash une fois tous les ajustements d'environnement effectués; vous pouvez mettre à jour ce script avec les autres commandes pertinentes que vous souhaitez exécuter.

0
user5359531