Vraag Voer een interactieve bash-subshell uit met initiële opdrachten zonder direct terug te keren naar de ("super") shell


Ik wil een bash-subshell uitvoeren, (1) een paar commando's uitvoeren, (2) en dan in die subshell blijven om te doen wat ik wil. Ik kan elk van deze afzonderlijk doen:

  1. Voer het commando uit met -c vlag:

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

    het keert echter onmiddellijk terug naar de "super" -shell nadat de opdrachten zijn uitgevoerd. Ik kan ook gewoon een interactieve subshell uitvoeren:

  2. Start nieuw bash werkwijze:

    $> bash
    

    en het zal de subshell niet verlaten totdat ik het expliciet zeg ... maar ik kan geen initiële commando's uitvoeren. De dichtstbijzijnde oplossing die ik heb gevonden is:

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

    wat werkt, maar niet zoals ik wilde, omdat het de gegeven commando's in één subshell uitvoert en vervolgens een apart commando opent voor interactie.

Ik wil dit op één regel doen. Zodra ik de subshell afsluit, zou ik zonder incidenten terug moeten keren naar de normale 'super'-shell. Er moet een manier zijn ~~

NB: Wat ik niet vraag ...

  1. niet vragen waar je de bash man-pagina te pakken kunt krijgen
  2. niet vragen hoe initialen commando's van een bestand te lezen ... Ik weet hoe dit te doen, het is niet de oplossing die ik zoek
  3. niet geïnteresseerd in het gebruik van tmux of gnu scherm
  4. niet geïnteresseerd in het geven van context hieraan. Dat wil zeggen, de vraag is bedoeld als algemeen en niet voor een specifiek doel
  5. als het mogelijk is, wil ik geen tijdelijke oplossingen gebruiken die een beetje bereiken wat ik wil, maar op een "vuile" manier. Ik wil dit gewoon op één regel doen. Ik wil met name zoiets niet doen xterm -e 'ls'

62
2018-03-09 15:12


oorsprong


Ik kan me een Expect-oplossing voorstellen, maar het is niet het enige dat u wilt. Op welke manier is het exec bash oplossing niet geschikt voor u? - glenn jackman
@glennjackman sorry, ik ben niet bekend met het jargon. Wat is een "Expect-oplossing"? Ook de exec bash oplossing omvat twee afzonderlijke subshells. Ik wil een doorlopende subshell. - SABBATINI Luca
De schoonheid van exec is dat het vervangt de eerste subshell met de tweede, dus je bent maar 1 shell onder de ouder gelaten. Als uw initialisatieopdrachten omgevingsvariabelen instellen, zullen deze in de exec'ed-shell voorkomen. - glenn jackman
mogelijk hetzelfde stackoverflow.com/questions/7120426/... - Ciro Santilli 新疆改造中心 六四事件 法轮功
En het probleem met exec is dat u alles verliest wat niet via de omgeving wordt doorgegeven aan subshells, zoals niet-geëxporteerde variabelen, functies, aliassen, ... - Curt J. Sampson


antwoorden:


Dit kan eenvoudig worden gedaan met tijdelijke named pipes:

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

Krediet voor dit antwoord gaat naar de opmerking van Lie Ryan. Ik vond dit echt handig en het is minder opvalt in de opmerkingen, dus ik dacht dat het zijn eigen antwoord moest zijn.


58
2018-04-02 20:17



Dit betekent vermoedelijk dat $HOME/.bashrc wordt echter niet uitgevoerd. Het zou moeten worden opgenomen van de tijdelijke named pipe. - Hubro
Om te verduidelijken, zoiets als dit: bash --init-file <(echo ". \"$HOME/.bashrc\"; ls; pwd") - Hubro
Dit is zo vies, maar het werkt. Ik kan niet geloven dat bash dit niet direct ondersteunt. - Pat Niemeyer
@Hubro, wat is het doel van de .  in het commando? Zonder dit krijg ik een "toestemming geweigerd" op mijn .bashrc, maar ik begrijp niet waarom, en hoe het . repareert het. - Gus
@Gus, de . is een synoniem voor de source commando: ss64.com/bash/source.html. - Jonathan Potter


Je kunt dit op een rotonde doen met een tijdelijk bestand, hoewel er twee regels nodig zijn:

echo "ls; pwd" > initfile
bash --init-file initfile

9
2018-03-09 16:21



Voor een leuk effect kun je het tijdelijke bestand zelf verwijderen door het op te nemen rm $BASH_SOURCE in het. - Eduardo Ivanec
Eduardo, bedankt. Dat is een mooie oplossing, maar ... zeg je dat dit niet kan zonder met I / O van bestanden te maken. Er zijn duidelijke redenen waarom ik dit liever als een op zichzelf staand commando zou houden, want vanaf het moment dat bestanden in de mix komen, moet ik me zorgen gaan maken over het maken van willekeurige tijdelijke bestanden en vervolgens, zoals je al zei, ze verwijderen. Het vereist gewoon zoveel meer moeite op deze manier als ik rigoureus wil zijn. Vandaar de wens voor een meer minimalistische, elegante oplossing. - SABBATINI Luca
@SABBATINILuca: Ik zeg zoiets niet. Dit is slechts een manier, en mktemp lost het tijdelijke bestand probleem op zoals @cjc opmerkt. Bash kon ondersteuning voor het lezen van de init-commando's van stdin, maar voor zover ik weet, is dit niet het geval. Specyfing - als init-bestand en piping werkt de helft, maar Bash wordt dan afgesloten (waarschijnlijk omdat het de pijplijn heeft gedetecteerd). De elegante oplossing, IMHO, is om exec te gebruiken. - Eduardo Ivanec
Negeert dit ook niet je normale bash-initialisatie? @SABBATINILuca Wat probeer je hiermee te bereiken waarom moet je een shell auto-spawnen, voer dan een aantal commando's uit en houd die shell open? - Iain
Dit is een oude vraag, maar Bash kan tijdelijke named pipes maken met behulp van de volgende syntaxis: bash --init-file <(echo "ls; pwd"). - Lie Ryan


Probeer dit in plaats daarvan:

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

$SHELL  Het maakt de shell open in de interactieve modus, wachtend op een afsluiting exit.


4
2018-02-23 03:50



Ter informatie: dit opent achteraf een nieuwe shell, dus als een van de opdrachten de huidige shell-status beïnvloedt (bijvoorbeeld een bestand), werkt het misschien niet zoals verwacht - ThiefMaster


De "Expect-oplossing" waar ik naar verwees was het programmeren van een bash-shell met de Verwacht de programmeertaal:

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

Je zou zo lopen als: ./subshell.exp "ls; pwd"


1
2018-03-09 19:56



Ik denk dat dit ook het voordeel zou hebben om de commando's in de geschiedenis te registreren, heb ik ongelijk? Ook ben ik benieuwd of bashrc / profile in dit geval wordt uitgevoerd? - muhuk


Als sudo -E bash werkt niet, ik gebruik het volgende, dat tot nu toe aan mijn verwachtingen heeft voldaan:

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

Ik heb HOME = $ HOME ingesteld omdat ik wil dat mijn nieuwe sessie THUIS heeft ingesteld op de HOME van mijn gebruiker, in plaats van op de HOME-hoofdmap van de root, wat standaard op sommige systemen gebeurt.


1
2017-09-16 15:07





minder elegant dan --init-file, maar misschien meer instrumentabel:

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

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

0
2018-04-17 01:37





Waarom geen native subshells gebruiken?

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

Als u commando's tussen haakjes plaatst, maakt bash een subproces om deze opdrachten uit te voeren, zodat u bijvoorbeeld de omgeving kunt wijzigen zonder de bovenliggende shell te beïnvloeden. Dit is in principe beter leesbaar als het bash -c "ls; pwd; exec $BASH".

Als dat er nog steeds breed uit ziet, zijn er twee opties. Een daarvan is om dit fragment als een functie te hebben:

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

Een andere is om te maken exec $BASH korter:

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

Ik persoonlijk vind het leuk R meer benaderen, omdat spelen met ontsnappingsstrings niet nodig is.


0
2017-08-14 21:51