Bash'i çağırmak, yeni kabuk içinde komutları çalıştırmak ve ardından kontrolü kullanıcıya nasıl geri vermek?


86

Bu ya gerçekten basit ya da çok karmaşık olmalı, ancak bununla ilgili hiçbir şey bulamadım ... Yeni bir bash örneği açmaya çalışıyorum, sonra içinde birkaç komut çalıştırıyorum ve kontrolü o içindeki kullanıcıya geri veriyorum aynı örnek .

Denedim:

$ bash -lic "some_command"

ancak bu some_command, yeni örnek içinde yürütülür ve ardından onu kapatır. Açık kalmasını istiyorum.

Cevapları etkileyebilecek bir ayrıntı daha: eğer bunu çalıştırabilirsem, onu .bashrctakma ad (lar) olarak kullanacağım , bu yüzden bir aliasuygulama için bonus puanlar !


Takma adlarla ilgili son kısmı anlamıyorum. Takma adlar geçerli kabuğun bağlamında çalışır, bu nedenle çözmenizi istediğiniz soruna sahip olmazlar (ancak genellikle, işlevler bir dizi nedenden ötürü diğer adlardan daha iyidir).
üçlü

2
alias shortcut='bash -lic "some_command"'.Bashrc dosyama benzer bir şey koyabilmek ve daha sonra çalıştırıp shortcutiçinde some_commandzaten çalıştırılmış yeni bir kabuğa sahip olmak istiyorum .
Félix Saparelli

Yanıtlar:


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

geçici dosyaların oluşturulmasını ortadan kaldırır. Diğer sitelerde soru:


Bunu Windows 10'da yapmaya çalıştım (toplu bir "başlangıç" dosyası / komut dosyası yazmaya çalışıyorum) ve anladımThe system cannot find the file specified.
Doctor Blue

1
@Scott adım adım hata ayıklama: 1) Çalışıyor mu cat ~/.bashrc? 2) Çalışıyor mu cat <(echo a)? 3) Çalışıyor mu bash --rcfile somefile?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
@Scott WSL'de bu sorunla karşılaştık (bash'ı bir başlangıç ​​toplu iş dosyasından başlatıyoruz). Çözümümüz, bazı karakterlerden kaçmaktı, böylece toplu iş onu anlayabilir: bash.exe --rcfile ^<^(echo 'source $HOME/.bashrc; ls; pwd'^)Windows komut isteminden çalıştırılmalıdır.
user2561747

sudo bash --init-file <(echo "ls; pwd")Veya gibi kullanıcı değiştirmeyle çalışmasını sağlamanın bir yolu var mı sudo -iu username bash --init-file <(echo "ls; pwd")?
jeremysprofile


40

Bu geç bir cevap, ama aynı problemi yaşadım ve Google beni bu sayfaya gönderdi, bu yüzden burada tamlık için problemi nasıl aştığım.

Anladığım kadarıyla bashorijinal posterin yapmak istediği şeyi yapma seçeneği yok. -cKomutlar çalıştırıldıktan sonra seçeneği her zaman dönecektir.

Bozuk çözüm : Bu konudaki en basit ve açık girişim şudur:

bash -c 'XXXX ; bash'

Bu kısmen işe yarıyor (ekstra bir alt kabuk katmanı olsa da). Ancak sorun şu ki, bir alt kabuk dışa aktarılan ortam değişkenlerini miras alırken, takma adlar ve işlevler miras alınmaz. Yani bu bazı şeyler için işe yarayabilir, ancak genel bir çözüm değildir.

Daha da iyisi : Bunu aşmanın yolu, dinamik olarak bir başlangıç ​​dosyası oluşturmak ve bu yeni başlatma dosyasıyla bash'ı çağırmak, yeni init dosyanızın ~/.bashrcgerekirse normal dosyanızı çağırdığından emin olmaktır .

# 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

İşin güzel yanı, geçici init dosyasının kullanıldığı anda kendini silecek ve doğru şekilde temizlenmeme riskini azaltacak.

Not: / etc / bashrc'nin genellikle oturum açmayan normal bir kabuğun parçası olarak çağrıldığından emin değilim. Eğer öyleyse, / etc / bashrc ve sizin ~/.bashrc.


Bunu rm -f $TMPFILEyapan şey. Bunun için sahip olduğum kullanım durumunu gerçekten hatırlamıyorum ve şimdi daha gelişmiş otomasyon teknikleri kullanıyorum, ancak bu, gidilecek yol!
Félix Saparelli

8
Süreç ikameli tmp dosyası yokbash --rcfile <(echo ". ~/.bashrc; a=b")
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

3
Geçici dosya yalnızca Bash betiğiniz başarılı bir şekilde çalışırsa temizlenir. Daha sağlam bir çözüm kullanmak olacaktır trap.
2016

5

--rcfileBash'e, seçtiğiniz bir dosyayı okumasını sağlamak için geçebilirsiniz . Bu dosya sizin yerine okunacak .bashrc. (Bu bir sorunsa, ~/.bashrcdiğer komut dosyasından kaynak .)

Düzenleme: Dolayısıyla, içindeki maddelerle yeni bir kabuk başlatmak için bir işlev ~/.more.shşunun gibi görünür:

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

... ve .more.shkabuk başladığında yürütmek istediğiniz komutlara sahip olursunuz. (Sanırım ayrı bir başlangıç ​​dosyasından kaçınmak zarif olacaktır - standart girişi kullanamazsınız çünkü bu durumda kabuk etkileşimli olmayacaktır, ancak geçici bir konumdaki buradaki bir belgeden bir başlangıç ​​dosyası oluşturabilir ve sonra okuyabilirsiniz.)


3

Komut dosyasını çalıştırmak yerine kaynak kullanarak istediğiniz işlevselliği elde edebilirsiniz. Örneğin:

$ cat script
cmd1
cmd2
$. senaryo
$ bu noktada cmd1 ve cmd2 bu kabuğun içinde çalıştırıldı

Hmm. Bu işe yarıyor, ancak her şeyi bir içine yerleştirmenin bir yolu yok alias=''mu?
Félix Saparelli

1
Asla takma ad kullanmayın; bunun yerine işlevleri kullanın. Şunları koyabilirsiniz: 'foo () {cmd1; cmd2; } 'başlatma betiklerinizin içinde ve sonra foo'nun çalıştırılması, mevcut kabuktaki iki komutu çalıştıracaktır. Bu çözümlerden hiçbirinin size yeni bir kabuk vermediğini unutmayın, ancak 'exec bash' ve sonra 'foo' yapabilirsiniz
William Pursell

1
Asla asla Deme. takma adların yeri var
glenn jackman

Takma ad yerine bir işlevin kullanılamayacağı bir kullanım durumu örneği var mı?
William Pursell

3

Bunun ~/.bashrcgibi bir bölüme ekleyin :

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

Ardından alt kabuğa başlayabilirsiniz sub.


Özgünlük için puanlar. Takma kendisi muhtemelen daha iyi gibidir env subshell=true bash(sızdırabiliriz olmayacak şekilde $subshelltakip eden komutlara): kullanma env vs ihracat kullanarak .
Félix Saparelli

Haklısın. Ama onsuz da çalıştığını fark ettim env. Tanımlanıp tanımlanmadığını kontrol etmek için echo $subshell( env | grep subshellsizin kullanmak yerine ) kullanıyorum.
dashohoxha

3

Kabul edilen cevap gerçekten yardımcı oldu! Sadece bu işlem ikamesinin (yani <(COMMAND)) bazı kabuklarda desteklenmediğini eklemek için (örneğin, dash).

Benim durumumda, bir kabuğu başlatmak ve seçilen Python sanal ortamını etkinleştirmek için Thunar dosya yöneticisinde özel bir eylem (temelde tek satırlık bir kabuk betiği) oluşturmaya çalışıyordum. İlk denemem şuydu:

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

%fThunar tarafından işlenen sanal ortama giden yol nerede . Bir hata aldım (komut satırından Thunar'ı çalıştırarak):

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

Sonra fark ettim ki sh(esasen dash) süreç ikamesini desteklemiyor.

Benim çözümüm, bashfazladan bir kabuk seviyesi pahasına, en üst düzeyde işlem ikamesini yorumlamaktı:

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

Alternatif olarak, burada-belgeyi kullanmayı denedim dashama başarılı olamadım. Gibi bir şey:

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

Not: Yorum göndermek için yeterli itibarım yok, moderatörler lütfen çekinmeden yorumlara taşıyabilir veya bu soruya yardımcı olmazsa kaldırabilir.


Eğer biri tepede bash istemiyorsa (veya sahip değilse), süreç ikamesini doğrudan uygulamanın çeşitli belgelenmiş yolları vardır, çoğunlukla fifos ile.
Félix Saparelli

2

Daveraja'nın cevabına göre , işte amacı çözecek bir bash betiği.

C-kabuğu kullanıyorsanız ve aşağıdaki gibi C-kabuğu bağlamından / penceresinden ayrılmadan bir komut yürütmek istiyorsanız bir durumu düşünün:

Yürütülecek komut : Geçerli dizinde yalnızca * .h, * .c dosyalarında yinelemeli olarak 'Test' kelimesini tam olarak arayın

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

Çözüm 1 : C-kabuğundan bash içine girin ve komutu yerine

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

2.Çözüm : İstenen komutu bir metin dosyasına yazın ve bash kullanarak çalıştırın

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

3. Çözüm : Bash kullanarak aynı satırda komutu çalıştırın

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

Çözüm 4 : Bir sciprt (tek seferlik) oluşturun ve gelecekteki tüm komutlar için kullanabilirsiniz

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

Komut dosyası aşağıdaki gibidir,

#!/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
Bu, tamamen farklı bir problem için tamamen farklı bir cevaptır. (Bunu çözmen çok güzel, ama bu konu bu konuya ait değil. Eğer bu sitede yoksa, ikinci cümleninle yeni bir soru açmayı, ardından hemen geri kalanıyla cevaplamayı düşün ... bu şekilde ele alınmalıdır ☺) Ayrıca, geçici bir dosya oluşturmadan bunu yapmanın bir yolu için Ciro Santilli'nin cevabına bakın.
Félix Saparelli

Bu soruyu yanıtlama girişimi olmadığı için yanıt değil olarak işaretlendi (ancak farklı bir soruyu yanıtlamak için gerçekten harika bir girişim!)
Charles Duffy

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

İşlem ikamesi kullanamıyorsanız veya kullanmak istemiyorsanız:

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

Diğer yol:

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

sh-yalnızca yol ( dash, busybox):

$ ENV=script sh

İyi alternatifler, ancak ENV=scripten üstteki cevabın bariz bir şekilde kopyalanması ile karıştırılmamak için üst bölümün (bölümden önce ) kaldırılması veya yeniden ifade edilmesi yararlı olacaktır.
Félix Saparelli

@ FélixSaparelli Dürüst olmak gerekirse, ENV+ shbiri dışındaki tüm çözümler diğer cevaplarda mevcut, ancak çoğunlukla tonlarca metne gömülü veya gereksiz / gereksiz kodla çevrili. Kısa ve net bir özetin herkese faydalı olacağına inanıyorum.
x-yuri

0

İşte bir başka (çalışan) değişken:

Bu yeni bir gnome terminali açar, ardından yeni terminalde bash çalıştırır. Önce kullanıcının rc dosyası okunur, ardından ls -laetkileşimli hale gelmeden önce yeni kabuğa yürütülmesi için bir komut gönderilir. Son yankı, yürütmeyi bitirmek için gereken fazladan bir satırsonu ekler.

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

Ayrıca bazen daha iyi yönlendirme için terminali renklendirmeyi de yararlı buluyorum.

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

-1

Bir arka plan kabuğunda komutları yürütme

Sadece ekle & komutun sonuna , örneğin:

bash -c some_command && another_command &
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.