Chạy một lớp con bash tương tác với các lệnh ban đầu mà không cần quay lại trình bao (siêu siêu phẩm) ngay lập tức


87

Tôi muốn chạy một bash subshell, (1) chạy một vài lệnh, (2) và sau đó vẫn ở trong subshell đó để làm như tôi muốn. Tôi có thể làm từng việc một:

  1. Chạy lệnh bằng -ccờ:

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

    tuy nhiên, nó ngay lập tức trở về shell "super" sau khi các lệnh được thực thi. Tôi cũng có thể chạy một subshell tương tác:

  2. Bắt đầu bashquy trình mới :

    $> bash

    và nó sẽ không thoát khỏi subshell cho đến khi tôi nói rõ ràng như vậy ... nhưng tôi không thể chạy bất kỳ lệnh ban đầu nào. Giải pháp gần nhất tôi tìm thấy là:

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

    hoạt động, nhưng không phải theo cách tôi muốn, vì nó chạy các lệnh đã cho trong một lớp con, sau đó mở một lệnh riêng để tương tác.

Tôi muốn làm điều này trên một dòng duy nhất. Khi tôi thoát khỏi lớp con, tôi nên quay lại lớp vỏ "siêu" thông thường mà không gặp sự cố. Phải có một cách ~ ~

NB: Điều tôi không hỏi ...

  1. không hỏi nơi để có được một trang bash man
  2. không hỏi làm thế nào để đọc các lệnh khởi tạo từ một tệp ... Tôi biết cách thực hiện điều này, đó không phải là giải pháp tôi đang tìm kiếm
  3. không quan tâm đến việc sử dụng màn hình tmux hoặc gnu
  4. không quan tâm đến việc đưa ra bối cảnh này. Tức là, câu hỏi có nghĩa là chung chung, và không nhằm mục đích cụ thể
  5. nếu có thể, tôi muốn tránh sử dụng các cách giải quyết khác để thực hiện những gì tôi muốn, nhưng theo cách "bẩn". Tôi chỉ muốn làm điều này trên một dòng duy nhất. Đặc biệt, tôi không muốn làm một cái gì đó nhưxterm -e 'ls'

Tôi có thể tưởng tượng một giải pháp Mong đợi, nhưng nó hầu như không phải là một lớp lót mà bạn muốn. Cách nào là exec bashgiải pháp không phù hợp với bạn?
glenn jackman

@glennjackman xin lỗi, tôi không quen thuộc với biệt ngữ. "Giải pháp mong đợi" là gì? Ngoài ra, exec bashgiải pháp liên quan đến hai subshells riêng biệt. Tôi muốn một subshell liên tục.
SABBATINI Luca

3
Cái hay của execnó là nó thay thế lớp con đầu tiên bằng cái thứ hai, vì vậy bạn chỉ còn lại 1 vỏ bên dưới cha mẹ. Nếu các lệnh khởi tạo của bạn đặt các biến môi trường, chúng sẽ tồn tại trong trình bao thực thi.
glenn jackman


2
Và vấn đề execlà bạn mất bất cứ thứ gì không được truyền vào các mạng con thông qua môi trường, chẳng hạn như các biến không xuất, hàm, bí danh, ...
Curt J. Sampson

Câu trả lời:


77

Điều này có thể dễ dàng thực hiện với các đường ống được đặt tên tạm thời :

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

Tín dụng cho câu trả lời này đi đến nhận xét từ Lie Ryan . Tôi thấy điều này thực sự hữu ích và nó ít được chú ý trong các bình luận, vì vậy tôi nghĩ nó nên là câu trả lời của riêng mình.


9
Điều này có lẽ có nghĩa $HOME/.bashrclà không được thực hiện mặc dù. Nó sẽ phải được bao gồm từ các ống được đặt tên tạm thời.
Hubro

9
Để làm rõ, một cái gì đó như thế này:bash --init-file <(echo ". \"$HOME/.bashrc\"; ls; pwd")
Hubro

5
Điều này là quá thô thiển nhưng nó làm việc. Tôi không thể tin bash không hỗ trợ điều này trực tiếp.
Pat Niemeyer

2
@Gus, .là từ đồng nghĩa của sourcelệnh: ss64.com/bash/source.html .
Jonathan Potter

1
Có cách nào để làm cho nó hoạt động với chuyển đổi người dùng, chẳng hạn như sudo bash --init-file <(echo "ls; pwd")hoặc sudo -iu username bash --init-file <(echo "ls; pwd")?
jeremysprofile

10

Bạn có thể thực hiện việc này theo cách vòng qua với tệp tạm thời, mặc dù sẽ có hai dòng:

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

Để có hiệu ứng đẹp, bạn có thể làm cho tệp tạm thời tự xóa bằng cách đưa rm $BASH_SOURCEvào nó.
Eduardo Ivanec

Eduardo, cảm ơn bạn. Đó là một giải pháp tốt, nhưng ... bạn có nói rằng điều này không thể được thực hiện mà không cần phải sử dụng tập tin I / O. Có những lý do rõ ràng tại sao tôi muốn giữ điều này như một lệnh độc lập, bởi vì các tệp thời điểm được trộn vào tôi sẽ phải bắt đầu lo lắng về cách tạo các tệp tạm thời ngẫu nhiên và sau đó, như bạn đã đề cập, xóa chúng. Nó chỉ đòi hỏi nhiều nỗ lực hơn theo cách này nếu tôi muốn nghiêm ngặt. Do đó mong muốn cho một giải pháp tối giản hơn, thanh lịch.
SABBATINI Luca

1
@SABBATINILuca: Tôi không nói bất cứ điều gì như thế. Đây chỉ là một cách và mktempgiải quyết vấn đề tệp tạm thời như @cjc đã chỉ ra. Bash có thể hỗ trợ đọc các lệnh init từ stdin, nhưng theo như tôi có thể nói thì không. Specyfing -dưới dạng tệp init và chuyển chúng thành một nửa hoạt động, nhưng Bash sau đó thoát ra (có lẽ vì nó đã phát hiện ra đường ống). Giải pháp tao nhã, IMHO, là sử dụng exec.
Eduardo Ivanec

1
Điều này cũng không ghi đè khởi tạo bash bình thường của bạn? @SABBATINILuca bạn đang cố gắng đạt được điều gì tại sao bạn cần phải sinh ra một trình bao tự động chạy một số lệnh và sau đó giữ cho trình bao đó mở?
dùng9517

11
Đây là một câu hỏi cũ, nhưng Bash có thể tạo các ống có tên tạm thời bằng cách sử dụng cú pháp sau: bash --init-file <(echo "ls; pwd").
Lie Ryan

5

Hãy thử điều này thay thế:

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

$SHELL Nó làm cho shell mở trong chế độ tương tác, chờ kết thúc exit.


9
FYI, điều này sẽ mở một shell mới sau đó, vì vậy nếu bất kỳ lệnh nào ảnh hưởng đến trạng thái của shell hiện tại (ví dụ: tìm nguồn của tệp) thì nó có thể không hoạt động như mong đợi
ThiefMaster

3

"Giải pháp kỳ vọng" mà tôi đã đề cập đến là lập trình bash shell với ngôn ngữ lập trình 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"

Bạn sẽ chạy như thế: ./subshell.exp "ls; pwd"


Tôi đoán điều này sẽ có lợi thế khi đăng ký các lệnh trong lịch sử, tôi có sai không? Ngoài ra tôi tò mò nếu bashrc / profile được thực thi trong trường hợp này?
muhuk

Xác nhận rằng điều này cho phép bạn đặt các lệnh trong lịch sử, điều mà các giải pháp khác không có. Điều này rất tốt để bắt đầu một quy trình trong tệp .screenrc - nếu bạn thoát khỏi quy trình bắt đầu, bạn không đóng cửa sổ màn hình.
Dan Sandberg

1

Tại sao không sử dụng subshells bản địa?

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

Việc đóng các lệnh với dấu ngoặc đơn làm cho bash sinh ra một quy trình con để chạy các lệnh này, ví dụ, bạn có thể thay đổi môi trường mà không ảnh hưởng đến vỏ cha. Điều này về cơ bản là dễ đọc hơn tương đương với bash -c "ls; pwd; exec $BASH".

Nếu điều đó vẫn có vẻ dài dòng, có hai lựa chọn. Một là có đoạn mã này dưới dạng hàm:

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

Một cách khác là exec $BASHrút ngắn:

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

Cá nhân tôi thích Rcách tiếp cận hơn, vì không cần phải chơi với các chuỗi thoát.


1
Tôi không chắc có bất kỳ hang động nào sử dụng exec cho kịch bản mà OP có trong đầu hay không, nhưng đối với tôi đây là giải pháp tốt nhất được đề xuất, bởi vì nó sử dụng các lệnh bash đơn giản mà không có bất kỳ vấn đề nào thoát khỏi chuỗi.
Peter

1

Nếu sudo -E bashkhông hoạt động, tôi sử dụng như sau, đã đáp ứng mong đợi của tôi cho đến nay:

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

Tôi đặt HOME = $ HOME vì tôi muốn phiên mới của mình được đặt HOME thành HOME của người dùng, thay vì HOME của root, điều này xảy ra theo mặc định trên một số hệ thống.


0

ít thanh lịch hơn --init-file, nhưng có lẽ nhiều dụng cụ hơn:

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

while read -a commands
do
    eval ${commands[@]}
done
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.