Jalankan subshell bash interaktif dengan perintah awal tanpa segera kembali ke shell ("super")


87

Saya ingin menjalankan subshell bash, (1) menjalankan beberapa perintah, (2) dan kemudian tetap dalam subshell itu untuk melakukan sesukaku. Saya dapat melakukan masing-masing ini secara individual:

  1. Jalankan perintah menggunakan -cflag:

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

    namun, ia segera kembali ke shell "super" setelah perintah dijalankan. Saya juga bisa menjalankan subkulit interaktif:

  2. Mulai bashproses baru :

    $> bash

    dan itu tidak akan keluar dari subkulit sampai saya katakan secara eksplisit ... tapi saya tidak bisa menjalankan perintah awal. Solusi terdekat yang saya temukan adalah:

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

    yang berfungsi, tetapi tidak seperti yang saya inginkan, karena menjalankan perintah yang diberikan dalam satu subkulit, dan kemudian membuka yang terpisah untuk interaksi.

Saya ingin melakukan ini pada satu baris. Setelah saya keluar dari subkulit, saya harus kembali ke shell "super" biasa tanpa insiden. Pasti ada jalan ~~

NB: Apa yang tidak saya tanyakan ...

  1. tidak bertanya di mana untuk mendapatkan halaman bash man
  2. tidak bertanya bagaimana membaca menginisialisasi perintah dari file ... Saya tahu bagaimana melakukan ini, itu bukan solusi yang saya cari
  3. tidak tertarik menggunakan layar tmux atau gnu
  4. tidak tertarik memberi konteks untuk ini. Yaitu, pertanyaannya dimaksudkan untuk bersifat umum, dan bukan untuk tujuan tertentu
  5. jika mungkin, saya ingin menghindari menggunakan solusi yang mencapai apa yang saya inginkan, tetapi dengan cara "kotor". Saya hanya ingin melakukan ini dalam satu baris. Secara khusus, saya tidak ingin melakukan sesuatu sepertixterm -e 'ls'

Saya bisa membayangkan solusi Harapkan, tapi itu bukan yang Anda inginkan. Dalam hal apa exec bashsolusinya tidak cocok untuk Anda?
glenn jackman

@ glennjackman maaf, saya tidak terbiasa dengan jargon. Apa itu "Harapkan solusi"? Juga, exec bashsolusinya melibatkan dua subkulit yang terpisah. Saya ingin satu subkulit terus menerus.
SABBATINI Luca

3
Keindahannya execadalah ia mengganti subshell pertama dengan yang kedua, jadi Anda hanya meninggalkan 1 shell di bawah induknya. Jika perintah inisialisasi Anda mengatur variabel lingkungan, mereka akan ada di shell yang dijalankan.
glenn jackman


2
Dan masalahnya execadalah Anda kehilangan apa pun yang tidak diturunkan ke subkulit melalui lingkungan, seperti variabel yang tidak diekspor, fungsi, alias, ...
Curt J. Sampson

Jawaban:


77

Ini dapat dengan mudah dilakukan dengan pipa bernama sementara :

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

Penghargaan untuk jawaban ini diberikan untuk komentar dari Lie Ryan . Saya menemukan ini sangat berguna, dan kurang terlihat dalam komentar, jadi saya pikir itu harus menjadi jawabannya sendiri.


9
Ini mungkin berarti bahwa $HOME/.bashrcitu tidak dieksekusi sekalipun. Itu harus dimasukkan dari pipa bernama sementara.
Hubro

9
Untuk memperjelas, sesuatu seperti ini:bash --init-file <(echo ". \"$HOME/.bashrc\"; ls; pwd")
Hubro

5
Ini sangat kotor tetapi tidak berhasil. Saya tidak percaya bash tidak mendukung ini secara langsung.
Pat Niemeyer

2
@Gus, .ini adalah sinonim untuk sourceperintah: ss64.com/bash/source.html .
Jonathan Potter

1
Apakah ada cara untuk membuatnya berfungsi dengan beralih pengguna, seperti sudo bash --init-file <(echo "ls; pwd")atau sudo -iu username bash --init-file <(echo "ls; pwd")?
jeremysprofile

10

Anda dapat melakukan ini secara tidak langsung dengan file temp, meskipun akan mengambil dua baris:

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

Untuk efek yang bagus, Anda dapat membuat file temp menghapus sendiri dengan memasukkannya rm $BASH_SOURCE.
Eduardo Ivanec

Eduardo, terima kasih. Itu solusi yang bagus, tetapi ... apakah Anda mengatakan bahwa ini tidak dapat dilakukan tanpa harus mengutak-atik file I / O. Ada alasan yang jelas mengapa saya lebih suka menyimpan ini sebagai perintah yang lengkap, karena saat file masuk ke dalam campuran saya harus mulai khawatir tentang bagaimana membuat file temp acak dan kemudian, seperti yang Anda sebutkan, menghapusnya. Ini hanya membutuhkan upaya yang jauh lebih banyak dengan cara ini jika saya ingin menjadi keras. Oleh karena itu keinginan untuk solusi yang lebih minimalis dan elegan.
SABBATINI Luca

1
@ SABBATINILuca: Saya tidak mengatakan hal seperti itu. Ini hanya cara, dan mktempmemang memecahkan masalah file temp seperti yang ditunjukkan oleh @cjc. Bash dapat mendukung membaca perintah init dari stdin, tetapi sejauh yang saya tahu tidak. Specyfing -sebagai file init dan memipainya setengah berfungsi, tetapi Bash kemudian keluar (mungkin karena mendeteksi pipa). Solusi elegan, IMHO, adalah menggunakan exec.
Eduardo Ivanec

1
Bukankah ini juga menimpa inisialisasi bash normal Anda? @ SABBATINILuca apa yang ingin Anda capai dengan ini mengapa Anda perlu memunculkan shell secara otomatis menjalankan beberapa perintah dan kemudian membiarkan shell itu tetap terbuka?
user9517

11
Ini adalah pertanyaan lama, tetapi Bash dapat membuat pipa bernama sementara dengan menggunakan sintaks berikut: bash --init-file <(echo "ls; pwd").
Lie Ryan

5

Coba ini sebagai gantinya:

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

$SHELL Itu membuat shell terbuka dalam mode interaktif, menunggu penutupan exit.


9
FYI, ini membuka shell baru sesudahnya, jadi jika ada perintah yang mempengaruhi status shell saat ini (mis. Sumber file) itu mungkin tidak berfungsi seperti yang diharapkan
ThiefMaster

3

"Solusi yang diharapkan" yang saya maksudkan adalah pemrograman bash shell dengan bahasa pemrograman Expect :

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

Anda akan menjalankannya seperti: ./subshell.exp "ls; pwd"


Saya kira ini akan memiliki keuntungan mendaftarkan perintah dalam sejarah juga, apakah saya salah? Saya juga ingin tahu apakah bashrc / profile dieksekusi dalam kasus ini?
muhuk

Dikonfirmasi bahwa ini memungkinkan Anda untuk menempatkan perintah dalam riwayat, yang tidak dimiliki solusi lain. Ini bagus untuk memulai proses dalam file .screenrc - jika Anda keluar dari proses yang dimulai, Anda tidak menutup jendela layar.
Dan Sandberg

1

Mengapa tidak menggunakan subshell asli?

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

Menutup perintah dengan tanda kurung menjadikan bash menelurkan subproses untuk menjalankan perintah ini, jadi Anda dapat, misalnya, mengubah lingkungan tanpa memengaruhi shell induk. Ini pada dasarnya lebih mudah dibaca setara dengan bash -c "ls; pwd; exec $BASH".

Jika itu masih terlihat verbose, ada dua opsi. Salah satunya adalah memiliki cuplikan ini sebagai fungsi:

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

Cara lain adalah membuat exec $BASHlebih pendek:

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

Saya pribadi lebih suka Rpendekatan, karena tidak perlu bermain dengan melarikan diri string.


1
Saya tidak yakin apakah ada peringatan yang menggunakan exec untuk skenario yang ada dalam pikiran OP, tetapi bagi saya ini adalah solusi terbaik yang diajukan, karena ia menggunakan perintah bash sederhana tanpa string yang keluar dari masalah.
Peter

1

Jika sudo -E bashtidak berhasil, saya menggunakan yang berikut ini, yang sejauh ini telah memenuhi harapan saya:

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

Saya mengatur HOME = $ HOME karena saya ingin sesi baru saya memiliki HOME yang diatur ke HOME pengguna saya, daripada HOME root, yang terjadi secara default pada beberapa sistem.


0

kurang elegan daripada --init-file, tapi mungkin lebih instrumental:

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

while read -a commands
do
    eval ${commands[@]}
done
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.