(“Super”) kabuğuna hemen geri dönmeden başlangıç ​​komutlarıyla etkileşimli bir bash alt kabuğu çalıştır


86

Bir bash alt kabuğu çalıştırmak, (1) birkaç komut çalıştırmak, (2) ve sonra da istediğim gibi yapmak için bu alt kabukta kalmak istiyorum. Bunların her birini ayrı ayrı yapabilirim:

  1. Flag kullanarak komutu çalıştırın -c:

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

    ancak komutlar uygulandıktan sonra derhal "süper" kabuğa döner. Ayrıca etkileşimli bir alt kabuk çalıştırabilirim:

  2. Yeni bashişleme başla :

    $> bash

    ve açıkça söyleyene kadar alt kabuktan çıkmayacak ... ama ilk komutları çalıştıramıyorum. Bulduğum en yakın çözüm:

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

    hangi çalışır, ancak istediğim şekilde değil, verilen komutları tek bir alt kabukta çalıştırır ve etkileşim için ayrı bir komut açar.

Bunu tek bir satırda yapmak istiyorum. Alt kabuktan çıktığımda, olaysız normal "süper" kabuğa geri dönmeliyim. Bir yolu olmalı ~ ~

Not: Ne sormuyorum ...

  1. bash man sayfasını nereden alacağınızı sormamak
  2. başlatma komutlarını bir dosyadan nasıl okuyacağımı sormama ... Bunun nasıl yapıldığını biliyorum, aradığım çözüm değil
  3. tmux veya gnu ekran kullanmakla ilgilenmiyor
  4. bağlam bağlamında ilgilenmiyorum. Başka bir deyişle, soru genel olmalı ve herhangi bir özel amaç için değil.
  5. mümkünse, istediğimi elde etmek için bu tür geçici çözümleri kullanmaktan kaçınmak istiyorum, ancak "kirli" bir şekilde. Bunu sadece tek bir satırda yapmak istiyorum. Özellikle, böyle bir şey yapmak istemiyorumxterm -e 'ls'

Bir Expect çözümü hayal edebiliyorum, ancak bu istediğiniz tek astar değil. exec bashÇözüm ne şekilde sizin için uygun değil?
glenn jackman

@glennjackman üzgünüm, jargonu bilmiyorum. "Beklenti çözümü" nedir? Ayrıca, exec bashçözüm iki ayrı alt kabuk içerir. Sürekli bir deniz kabuğu istiyorum.
SABBATINI Luca

3
Güzelliği exectam o yerini ikinci ile birinci altkabuk, bu nedenle sadece ebeveyn altında 1 kabuk kalır geriye. Eğer başlatma komutlarınız ortam değişkenlerini ayarladıysa, bunlar çalıştırılan kabukta bulunacaktır.
glenn jackman


2
Ve sorun execşu ki, dışa aktarılmayan değişkenler, fonksiyonlar, takma adlar, gibi alt kabuklara aktarılmayan herhangi bir şeyi kaybetmeniz ...
Curt J. Sampson

Yanıtlar:


77

Bu geçici adlandırılmış borular ile kolayca yapılabilir :

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

Bu cevabın karşılığı Lie Ryan'ın yorumuna gidiyor . Bunu gerçekten yararlı buldum ve yorumlarda daha az farkediyorum, bu yüzden kendi cevabı olmalıydı.


9
Bu, muhtemelen bunun $HOME/.bashrcyapılmayacağı anlamına geliyor . Geçici olarak adlandırılmış yöneltmeden dahil edilmesi gerekirdi.
Hubro,

9
Netleştirmek için, böyle bir şey:bash --init-file <(echo ". \"$HOME/.bashrc\"; ls; pwd")
Hubro

5
Bu çok iğrenç ama işe yarıyor. Bash'in bunu doğrudan desteklemediğine inanamıyorum.
Pat Niemeyer

2
@Gus, komutun .eş anlamlısıdır source: ss64.com/bash/source.html .
Jonathan Potter

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

10

İki satır sürecek olmasına rağmen, bunu geçici bir dosyayla dolambaçlı bir şekilde yapabilirsiniz:

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

Güzel bir etki için temp dosyası içine dahil rm $BASH_SOURCEederek kendini kaldırmasını sağlayabilirsiniz .
Eduardo Ivanec

Eduardo, teşekkür ederim. Güzel bir çözüm, ama ... bunun G / Ç dosyasıyla uğraşmadan yapamayacağınızı mı söylüyorsunuz ? Bunu kendi kendine yeten bir komut olarak tutmayı tercih etmemin açık nedenleri var, çünkü dosyalar karıştığı andan itibaren rastgele geçici dosyaları nasıl oluşturacağınız konusunda endişelenmeye başlayacağım ve daha sonra da belirttiğiniz gibi onları silmeye başlayacağım. Zorlu olmak istersem, bu şekilde çok daha fazla çaba gerektirir. Dolayısıyla daha minimalist, zarif bir çözüm arzusu.
SABBATINI Luca

1
@ SABBATINILuca: Böyle bir şey söylemiyorum. Bu sadece bir yoldur ve mktempgeçici dosya sorununu, cjc'nin işaret ettiği gibi çözer. Bash olabilir stdin'den init komutları okuma desteklemek, ama bildiğim kadarıyla söyleyebilirim öyle değil. Init -dosyası olarak tanımlamak ve boruları yarı çalışır, ancak Bash sonra çıkar (muhtemelen boru hattını tespit ettiğinden). Zarif çözüm, IMHO, exec kullanmaktır.
Eduardo Ivanec

1
Bu, normal bash başlangıcınızı geçersiz kılmaz mı? @ SABBATINILuca bununla ne yapmaya çalışıyorsun, neden bazı komutları otomatik olarak çalıştırıp kabuk açmaya ihtiyacın var?
user9517

11
Bu eski bir soru, ancak Bash aşağıdaki sözdizimini kullanarak geçici olarak adlandırılmış yöneltmeler oluşturabilir: bash --init-file <(echo "ls; pwd").
Yalan Ryan

5

Bunun yerine şunu deneyin:

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

$SHELL Kabuğunu etkileşimli modda açıp kapatmayı bekliyor exit.


9
Bilginize, bu daha sonra yeni bir kabuk açar, bu nedenle komutlardan herhangi biri mevcut kabuğun durumunu etkilerse (örneğin bir dosya kaynağı), beklendiği gibi çalışmayabilir
ThiefMaster

3

Bahsettiğim "Expect çözümü", Expect programlama diliyle bir bash kabuğu programlıyor :

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

Böyle koşarsın: ./subshell.exp "ls; pwd"


Bunun da emirleri tarihe kaydetme avantajına sahip olacağını düşünüyorum, yanlış mıyım? Ayrıca bashrc / profile bu durumda yürütülürse merak ediyorum?
muhuk

Bunun, diğer çözümlerin yapmadığı komutları tarihe koymanıza izin verdiğini onayladı. Bir .screenrc dosyasında bir işlemi başlatmak için harika - başlatılan işlemden çıkarsanız, ekran penceresini kapatmazsınız.
Dan Sandberg

1

Neden yerel alt kabuk kullanmıyorsunuz?

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

Komutları parantez içine almak, bash'ın bu komutları çalıştırmak için alt işlemi başlatmasını sağlar; böylece, örneğin, kabuğu değiştirmeden çevreyi değiştirebilirsiniz. Bu temelde daha okunabilir eşdeğerdir bash -c "ls; pwd; exec $BASH".

Bu hala ayrıntılı görünüyorsa, iki seçenek vardır. Biri, bu pasajı bir işlev olarak kullanmaktır:

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

Başka bir exec $BASHkısaltmak için:

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

Ben şahsen Rdaha çok yaklaşmayı seviyorum , çünkü kaçan karakterlerle oynamaya gerek yok.


1
OP'nin göz önünde bulundurduğu senaryo için exec kullanan herhangi bir mağara olup olmadığından emin değilim, ancak benim için bu, önerilen herhangi bir dize kaçış sorunu olmadan düz bash komutları kullandığı için önerilen en iyi çözümdür.
Peter,

1

Çalışmazsa sudo -E bash, şu ana kadar beklentilerimi karşılayan aşağıdakileri kullanıyorum:

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

HOME'u $ $ HOME olarak ayarlıyorum çünkü yeni oturumumun bazı sistemlerde varsayılan olarak gerçekleşen root'un HOME'u yerine HOME'un kullanıcının HOME'una ayarlanmasını istiyorum.


0

daha az zarif --init-file, ama belki daha enstrümantal:

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

while read -a commands
do
    eval ${commands[@]}
done
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.