Jak wywołać bash, uruchamiać polecenia w nowej powłoce, a następnie oddać kontrolę użytkownikowi?


86

To musi być albo bardzo proste, albo bardzo złożone, ale nie mogłem nic na ten temat znaleźć ... Próbuję otworzyć nową instancję basha, następnie uruchomić w niej kilka poleceń i oddać kontrolę użytkownikowi znajdującemu się w niej ta sama instancja .

Próbowałem:

$ bash -lic "some_command"

ale to jest wykonywane some_commandwewnątrz nowej instancji, a następnie ją zamyka. Chcę, żeby było otwarte.

Jeszcze jeden szczegół, który może mieć wpływ na odpowiedzi: jeśli uda mi się to uruchomić, użyję go .bashrcjako aliasu (aliasów), więc dodatkowe punkty za aliaswdrożenie!


Nie rozumiem ostatniej części dotyczącej aliasów. Aliasy są wykonywane w kontekście bieżącej powłoki, więc nie mają problemu, o którego rozwiązanie prosisz (chociaż zazwyczaj funkcje są lepsze niż aliasy, z wielu powodów).
— tripleee

2
Chcę móc umieścić coś takiego alias shortcut='bash -lic "some_command"'w moim .bashrc, a następnie uruchomić shortcuti mieć nową powłokę z some_commandjuż uruchomioną.
— Félix Saparelli

Odpowiedzi:


63
bash --rcfile <(echo '. ~/.bashrc; some_command')

rezygnuje z tworzenia plików tymczasowych. Pytanie w innych witrynach:


Próbowałem to zrobić w systemie Windows 10 (próbuję napisać plik / skrypt wsadowy "startowy") i otrzymujęThe system cannot find the file specified.
— Doctor Blue,

1
@Scott debuguj krok po kroku: 1) Czy cat ~/.bashrcdziała? 2) Czy cat <(echo a)działa? 3) Czy bash --rcfile somefiledziała?
— Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
@Scott właśnie natknęliśmy się na ten problem na WSL (uruchamianie bash z pliku wsadowego startowego). Naszym rozwiązaniem było uniknięcie niektórych znaków, aby partia mogła to zrozumieć: bash.exe --rcfile ^<^(echo 'source $HOME/.bashrc; ls; pwd'^)powinna być uruchamiana z wiersza poleceń systemu Windows.
— user2561747

Czy istnieje sposób, aby działał z przełączaniem użytkowników, na przykład sudo bash --init-file <(echo "ls; pwd")lub sudo -iu username bash --init-file <(echo "ls; pwd")?
— jeremysprofile


39

To jest późna odpowiedź, ale miałem dokładnie ten sam problem i Google wysłał mnie na tę stronę, więc aby uzyskać kompletność, oto jak poradziłem sobie z problemem.

O ile wiem, bashnie ma możliwości zrobienia tego, co chciał zrobić oryginalny plakat. -cOpcja będzie zawsze wrócić po rozkazy zostały wykonane.

Zepsute rozwiązanie : Najprostsza i oczywista próba obejścia tego:

bash -c 'XXXX ; bash'

To częściowo działa (aczkolwiek z dodatkową warstwą podpowłoki). Jednak problem polega na tym, że podczas gdy powłoka podrzędna odziedziczy wyeksportowane zmienne środowiskowe, aliasy i funkcje nie są dziedziczone. Może to działać w niektórych przypadkach, ale nie jest ogólnym rozwiązaniem.

Lepiej : sposobem na obejście tego jest dynamiczne utworzenie pliku startowego i wywołanie bash z tym nowym plikiem inicjalizacyjnym, upewniając się, że nowy plik init wywołuje zwykły plik, ~/.bashrcjeśli to konieczne.

# 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

Fajną rzeczą jest to, że tymczasowy plik init usunie się, gdy tylko zostanie użyty, zmniejszając ryzyko, że nie zostanie poprawnie wyczyszczony.

Uwaga: nie jestem pewien, czy / etc / bashrc jest zwykle wywoływany jako część normalnej powłoki niezalogowanej. Jeśli tak, możesz chcieć pobrać / etc / bashrc, a także swój ~/.bashrc.


rm -f $TMPFILECo czyni go. Właściwie nie pamiętam przypadku użycia, który miałem do tego, a teraz używam bardziej zaawansowanych technik automatyzacji, ale to jest droga!
— Félix Saparelli

7
Brak plików tmp z podstawieniem procesubash --rcfile <(echo ". ~/.bashrc; a=b")
— Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

3
Plik tymczasowy zostanie wyczyszczony tylko wtedy, gdy skrypt Bash zostanie uruchomiony pomyślnie. Bardziej solidnym rozwiązaniem byłoby użycie trap.
— tripleee

5

Możesz przekazać --rcfileBashowi, aby odczytał wybrany plik. Ten plik zostanie odczytany zamiast twojego .bashrc. (Jeśli to problem, źródło ~/.bashrcz innego skryptu).

Edycja: Tak więc funkcja uruchamiająca nową powłokę z rzeczami ~/.more.shwyglądałaby mniej więcej tak:

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

... aw .more.shtobie byłyby polecenia, które chcesz wykonać podczas uruchamiania powłoki. (Przypuszczam, że elegancko byłoby uniknąć oddzielnego pliku startowego - nie można używać standardowego wejścia, ponieważ wtedy powłoka nie będzie interaktywna, ale można utworzyć plik startowy z dokumentu tutaj w tymczasowej lokalizacji, a następnie przeczytać go.)


3

Możesz uzyskać żądaną funkcjonalność, pozyskując skrypt zamiast go uruchamiać. na przykład:

Skrypt $ cat
cmd1
cmd2
$. scenariusz
$ w tym momencie cmd1 i cmd2 zostały uruchomione wewnątrz tej powłoki

Hmm. To działa, ale czy nie ma możliwości osadzenia wszystkiego w pliku alias=''?
— Félix Saparelli

1
Nigdy nie używaj aliasów; zamiast tego użyj funkcji. Możesz wstawić: 'foo () {cmd1; cmd2; } 'wewnątrz skryptów startowych, a następnie wykonanie foo spowoduje uruchomienie dwóch poleceń w bieżącej powłoce. Zauważ, że żadne z tych rozwiązań nie daje nowej powłoki, ale możesz wykonać „exec bash”, a następnie „foo”
— William Pursell

1
Nigdy nie mów nigdy. aliasy mają swoje miejsce
— glenn jackman

Czy istnieje przykład przypadku użycia, w którym funkcja nie może być używana zamiast aliasu?
— William Pursell,

3

Dołącz do ~/.bashrcsekcji takiej jak ta:

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

Następnie możesz rozpocząć podpowłokę za pomocą sub.


Punkty za oryginalność. Sam alias jest prawdopodobnie lepszy, ponieważ env subshell=true bash(ponieważ nie wycieknie to $subshelldo kolejnych poleceń): Używanie env vs. używanie eksportu .
— Félix Saparelli

Masz rację. Ale zauważyłem, że działa również bez env. Aby sprawdzić, czy jest zdefiniowany, czy nie, używam echo $subshell(zamiast env | grep subshelltego używasz).
— dashohoxha,

3

Przyjęta odpowiedź jest naprawdę pomocna! Wystarczy dodać, że podstawianie procesów (tj. <(COMMAND)) Nie jest obsługiwane w niektórych powłokach (np dash.).

W moim przypadku próbowałem utworzyć niestandardową akcję (w zasadzie jednoliniowy skrypt powłoki) w menedżerze plików Thunar, aby uruchomić powłokę i aktywować wybrane środowisko wirtualne Pythona. Moja pierwsza próba to:

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

gdzie %fjest ścieżka do wirtualnego środowiska obsługiwanego przez Thunara. Otrzymałem błąd (uruchamiając Thunara z linii poleceń):

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

Wtedy zdałem sobie sprawę, że mój sh(zasadniczo dash) nie obsługuje zastępowania procesów.

Moim rozwiązaniem było wywołanie bashna najwyższym poziomie w celu zinterpretowania podstawienia procesu kosztem dodatkowego poziomu powłoki:

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

Alternatywnie, próbowałem użyć dokumentu tutaj, dashale bez powodzenia. Coś jak:

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

PS: Nie mam wystarczającej reputacji, aby publikować komentarze, moderatorzy mogą swobodnie przenosić to do komentarzy lub usuwać, jeśli nie jest to pomocne w przypadku tego pytania.


Jeśli ktoś nie chce bash na wierzchu (lub go nie ma), istnieje wiele udokumentowanych sposobów bezpośredniej implementacji zastępowania procesów, głównie za pomocą FIFO.
— Félix Saparelli

2

Zgodnie z odpowiedzią daveraja , oto skrypt bash, który rozwiąże cel.

Rozważ sytuację, jeśli używasz powłoki C i chcesz wykonać polecenie bez opuszczania kontekstu / okna powłoki C w następujący sposób:

Polecenie do wykonania : Wyszukaj rekurencyjnie dokładnie słowo „Testing” w bieżącym katalogu tylko w plikach * .h, * .c

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

Rozwiązanie 1 : Wejdź do bash z powłoki C i wykonaj polecenie

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

Rozwiązanie 2 : Zapisz żądane polecenie w pliku tekstowym i wykonaj je za pomocą basha

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

Rozwiązanie 3 : Uruchom polecenie w tej samej linii, używając bash

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

Rozwiązanie 4 : Utwórz sciprt (jednorazowo) i używaj go do wszystkich przyszłych poleceń

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

Scenariusz jest następujący,

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

2
To zupełnie inna odpowiedź na zupełnie inny problem. (Fajnie, że to zrozumiałeś, ale nie należy to do tego wątku. Jeśli tego nie było na tej stronie, rozważ otwarcie nowego pytania z drugim zdaniem, a następnie od razu odpowiedz na nie resztą ... tak należy to załatwić see) Zobacz również odpowiedź Ciro Santilli, aby dowiedzieć się, jak zrobić to bez tworzenia pliku tymczasowego.
— Félix Saparelli

Oznaczona jako brak odpowiedzi, ponieważ nie jest to próba odpowiedzi na to pytanie (choć jest to naprawdę świetna próba odpowiedzi na inne!)
— Charles Duffy

0

Oto kolejny (działający) wariant:

Spowoduje to otwarcie nowego terminala gnome, a następnie w nowym terminalu uruchomi bash. Najpierw odczytywany jest plik rc użytkownika, a następnie polecenie ls -lajest wysyłane do wykonania do nowej powłoki, zanim stanie się interaktywna. Ostatnie echo dodaje dodatkowy znak nowej linii, który jest potrzebny do zakończenia wykonywania.

gnome-terminal -- bash -c 'bash --rcfile <( cat ~/.bashrc; echo ls -la ; echo)'

Przydaje mi się też czasem udekorowanie terminala, np. Kolorem dla lepszej orientacji.

gnome-terminal --profile green -- bash -c 'bash --rcfile <( cat ~/.bashrc; echo ls -la ; echo)'

-1

Wykonywanie poleceń w powłoce w tle

Wystarczy dopisać &na końcu polecenia np:

bash -c some_command && another_command &

-1
$ bash --init-file <(echo 'some_command')
$ bash --rcfile <(echo 'some_command')

Jeśli nie możesz lub nie chcesz korzystać z zastępowania procesów:

$ cat script
some_command
$ bash --init-file script

Inny sposób:

$ bash -c 'some_command; exec bash'
$ sh -c 'some_command; exec sh'

sh-tylko sposób ( dash, busybox):

$ ENV=script sh

Dobre alternatywy, ale korzystne byłoby ENV=scriptusunięcie lub przeformułowanie górnej sekcji (przed częścią), aby uniknąć pomylenia z rażącym kopiowaniem i wklejaniem pierwszej odpowiedzi.
— Félix Saparelli

@ FélixSaparelli Szczerze mówiąc, wszystkie rozwiązania, ale ENV+ shjeden są obecne w innych odpowiedziach, ale przeważnie są ukryte w tonach tekstu lub otoczone niepotrzebnym / nieistotnym kodem. Uważam, że krótkie, jasne podsumowanie byłoby korzystne dla wszystkich.
— x-yuri
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.