web-dev-qa-db-fra.com

Comment appeler bash, exécuter des commandes dans le nouveau shell, puis redonner le contrôle à l'utilisateur?

Cela doit être très simple ou très complexe, mais je n'ai rien trouvé à ce sujet ... J'essaie d'ouvrir une nouvelle instance bash, puis d'exécuter quelques commandes à l'intérieur et de redonner le contrôle à l'utilisateur à l'intérieur de cette même instance .

J'ai essayé:

$ bash -lic "some_command"

mais ceci exécute some_command dans la nouvelle instance, puis la ferme. Je veux que ça reste ouvert.

Un détail supplémentaire qui pourrait affecter les réponses: si je parviens à faire fonctionner cela, je l’utiliserai dans mon .bashrc comme alias (es), donc des points bonus pour une implémentation alias!

43
Félix Saparelli

C'est une réponse tardive, mais j'avais exactement le même problème et Google m'a envoyé sur cette page. Par souci d'exhaustivité, voici comment j'ai résolu le problème.

Autant que je sache, bash n'a pas la possibilité de faire ce que l'affiche originale voulait faire. L'option -c sera toujours renvoyée après l'exécution des commandes. 

Solution brisée : la tentative la plus simple et la plus évidente à ce sujet est la suivante:

bash -c 'XXXX ; bash'

Cela fonctionne en partie (mais avec une couche supplémentaire de sous-shell). Cependant, le problème est que, même si un sous-shell hérite des variables d'environnement exportées, les alias et les fonctions ne sont pas hérités. Cela peut donc fonctionner pendant quelque temps, mais ce n’est pas une solution générale.

Mieux : La solution consiste à créer dynamiquement un fichier de démarrage et à appeler bash avec ce nouveau fichier d’initialisation, en veillant à ce que votre nouveau fichier init appelle votre ~/.bashrc normal si nécessaire.

# Create a temporary file
TMPFILE=$(mktemp)

# Add stuff to the temporary file
echo "source ~/.bashrc" > $TMPFILE
echo "<other commands>" >> $TMPFILE
echo "rm -f $TMPFILE" >> $TMPFILE

# Start the new bash Shell 
bash --rcfile $TMPFILE

Ce qui est bien, c'est que le fichier d'initialisation temporaire se supprime dès qu'il est utilisé, ce qui réduit le risque qu'il ne soit pas nettoyé correctement.

Remarque: Je ne sais pas si/etc/bashrc est généralement appelé dans le cadre d'un shell normal sans connexion. Si c'est le cas, vous voudrez peut-être utiliser le répertoire/etc/bashrc ainsi que votre ~/.bashrc.

32
daveraja

Vous pouvez transmettre --rcfile à Bash pour lui faire lire le fichier de votre choix. Ce fichier sera lu à la place de votre .bashrc. (Si cela pose un problème, source ~/.bashrc à partir de l'autre script.)

Edit: Donc, une fonction permettant de démarrer un nouveau Shell avec les éléments de ~/.more.sh ressemblerait à quelque chose comme:

more() { bash --rcfile ~/.more.sh ; }

... et dans .more.sh vous auriez les commandes que vous voulez exécuter au démarrage du shell. (Je suppose qu'il serait élégant d'éviter un fichier de démarrage séparé. Vous ne pouvez pas utiliser une entrée standard car le shell ne sera pas interactif. Vous pouvez créer un fichier de démarrage à partir d'un document ici situé temporairement, puis le lire.)

4
tripleee

Vous pouvez obtenir la fonctionnalité de votre choix en recherchant le script au lieu de l'exécuter. par exemple:

 $ script de chat 
 cmd1 
 cmd2 
 $. script 
 $ à ce stade, cmd1 et cmd2 ont été exécutés à l'intérieur de ce shell 
2
William Pursell

Conformément à la réponse de daveraja , voici un script bash qui résoudra le problème.

Considérez une situation si vous utilisez C-Shell et que vous souhaitez exécuter une commande Sans quitter le contexte/la fenêtre de C-Shell comme suit,

Commande à exécuter: Rechercher le mot exact 'Testing' dans le répertoire actuel de manière récursive uniquement dans les fichiers * .h, * .c

grep -nrs --color -w --include="*.{h,c}" Testing ./

Solution 1: entrez bash à partir de C-Shell et exécutez la commande

bash
grep -nrs --color -w --include="*.{h,c}" Testing ./
exit

Solution 2: écrivez la commande souhaitée dans un fichier texte et exécutez-la à l'aide de bash

echo 'grep -nrs --color -w --include="*.{h,c}" Testing ./' > tmp_file.txt
bash tmp_file.txt

Solution 3: Exécute la commande sur la même ligne avec bash

bash -c 'grep -nrs --color -w --include="*.{h,c}" Testing ./'

Solution 4: Créer un sciprt (une fois) et l'utiliser pour toutes les commandes futures

alias ebash './execute_command_on_bash.sh'
ebash grep -nrs --color -w --include="*.{h,c}" Testing ./

Le script est comme suit,

#!/bin/bash
# =========================================================================
# References:
# https://stackoverflow.com/a/13343457/5409274
# https://stackoverflow.com/a/26733366/5409274
# https://stackoverflow.com/a/2853811/5409274
# https://stackoverflow.com/a/2853811/5409274
# https://www.linuxquestions.org/questions/other-%2Anix-55/how-can-i-run-a-command-on-another-Shell-without-changing-the-current-Shell-794580/
# https://www.tldp.org/LDP/abs/html/internalvariables.html
# https://stackoverflow.com/a/4277753/5409274
# =========================================================================

# Enable following line to see the script commands
# getting printing along with their execution. This will help for debugging.
#set -o verbose

E_BADARGS=85

if [ ! -n "$1" ]
then
  echo "Usage: `basename $0` grep -nrs --color -w --include=\"*.{h,c}\" Testing ."
  echo "Usage: `basename $0` find . -name \"*.txt\""
  exit $E_BADARGS
fi  

# Create a temporary file
TMPFILE=$(mktemp)

# Add stuff to the temporary file
#echo "echo Hello World...." >> $TMPFILE

#initialize the variable that will contain the whole argument string
argList=""
#iterate on each argument
for arg in "$@"
do
  #if an argument contains a white space, enclose it in double quotes and append to the list
  #otherwise simply append the argument to the list
  if echo $arg | grep -q " "; then
   argList="$argList \"$arg\""
  else
   argList="$argList $arg"
  fi
done

#remove a possible trailing space at the beginning of the list
argList=$(echo $argList | sed 's/^ *//')

# Echoing the command to be executed to tmp file
echo "$argList" >> $TMPFILE

# Note: This should be your last command
# Important last command which deletes the tmp file
last_command="rm -f $TMPFILE"
echo "$last_command" >> $TMPFILE

#echo "---------------------------------------------"
#echo "TMPFILE is $TMPFILE as follows"
#cat $TMPFILE
#echo "---------------------------------------------"

check_for_last_line=$(tail -n 1 $TMPFILE | grep -o "$last_command")
#echo $check_for_last_line

#if tail -n 1 $TMPFILE | grep -o "$last_command"
if [ "$check_for_last_line" == "$last_command" ]
then
  #echo "Okay..."
  bash $TMPFILE
  exit 0
else
  echo "Something is wrong"
  echo "Last command in your tmp file should be removing itself"
  echo "Aborting the process"
  exit 1
fi
1
Rohan Ghige

La réponse acceptée est vraiment utile! Juste pour ajouter que la substitution de processus (par exemple, <(COMMAND)) n'est pas prise en charge dans certains shells (par exemple, dash).

Dans mon cas, j'essayais de créer une action personnalisée (essentiellement un script Shell à une ligne) dans le gestionnaire de fichiers Thunar pour démarrer un shell et activer l'environnement virtuel sélectionné Python. Ma première tentative a été:

urxvt -e bash --rcfile <(echo ". $HOME/.bashrc; . %f/bin/activate;")

%f est le chemin d'accès à l'environnement virtuel géré par Thunar. J'ai une erreur (en lançant Thunar depuis la ligne de commande):

/bin/sh: 1: Syntax error: "(" unexpected

Ensuite, j'ai réalisé que ma sh (essentiellement dash) ne prend pas en charge la substitution de processus.

Ma solution a été d'invoquer bash au niveau supérieur pour interpréter la substitution de processus, au détriment d'un niveau supplémentaire de Shell:

bash -c 'urxvt -e bash --rcfile <(echo "source $HOME/.bashrc; source %f/bin/activate;")'

Alternativement, j'ai essayé d'utiliser here-document pour dash mais sans succès. Quelque chose comme:

echo -e " <<EOF\n. $HOME/.bashrc; . %f/bin/activate;\nEOF\n" | xargs -0 urxvt -e bash --rcfile

P.S .: Je n'ai pas assez de réputation pour publier des commentaires. Les modérateurs sont libres de le déplacer en commentaires ou de le supprimer s'il n'est pas utile pour cette question.

1
Haimo

Ajoutez à ~/.bashrc une section comme celle-ci:

if [ "$subshell" = 'true' ]
then
    # commands to execute only on a subshell
    date
fi
alias sub='subshell=true bash'

Ensuite, vous pouvez démarrer le sous-shell avec sub.

1
dashohoxha