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!

Félix Saparelli
źródło
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:

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło
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.

daveraja
źródło
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.)

tripleee
źródło
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
William Pursell
źródło
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.

dashohoxha
źródło
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.

Haimo
źródło
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
Rohan Ghige
źródło
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)'
user1656671
źródło
-1

Wykonywanie poleceń w powłoce w tle

Wystarczy dopisać &na końcu polecenia np:

bash -c some_command && another_command &
raphtlw
źródło
-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
x-yuri
źródło
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