Làm thế nào để gọi bash, chạy các lệnh bên trong trình bao mới và sau đó trao lại quyền kiểm soát cho người dùng?


86

Điều này phải thực sự đơn giản hoặc thực sự phức tạp, nhưng tôi không thể tìm thấy bất cứ điều gì về nó ... Tôi đang cố gắng mở một phiên bản bash mới, sau đó chạy một vài lệnh bên trong nó và trao lại quyền kiểm soát cho người dùng bên trong đó cùng một trường hợp .

Tôi đã thử:

$ bash -lic "some_command"

nhưng điều này thực thi some_commandbên trong thể hiện mới, sau đó đóng nó. Tôi muốn nó luôn mở.

Một chi tiết khác có thể ảnh hưởng đến câu trả lời: nếu tôi có thể làm cho điều này hoạt động, tôi sẽ sử dụng nó trong .bashrc(các) bí danh của mình , vì vậy điểm thưởng cho một aliastriển khai!


Tôi không hiểu phần cuối cùng về bí danh. Bí danh thực thi trong ngữ cảnh của trình bao hiện tại, vì vậy chúng không gặp vấn đề mà bạn đang yêu cầu để giải quyết (mặc dù thông thường, các hàm tốt hơn bí danh, vì một số lý do).
tripleee

2
Tôi muốn có thể đặt một cái gì đó giống như alias shortcut='bash -lic "some_command"'.bashrc của mình sau đó chạy shortcutvà có một trình bao mới some_commandđã chạy trong đó.
Félix Saparelli

Câu trả lời:


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

phân phối việc tạo các tệp tạm thời. Câu hỏi trên các trang web khác:


Tôi đã cố gắng để làm điều này trong Windows 10 (cố gắng để viết một lô "khởi động" tập tin / script), và tôi nhận đượcThe system cannot find the file specified.
Doctor xanh

1
@Scott gỡ lỗi nó từng bước: 1) Có cat ~/.bashrchoạt động không? 2) Không cat <(echo a)hoạt động? 3) Không bash --rcfile somefilehoạt động?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
@Scott, chúng tôi vừa gặp sự cố này trên WSL (khởi chạy bash từ tệp lô khởi động). Giải pháp của chúng tôi là thoát một số ký tự để lô có thể hiểu nó: bash.exe --rcfile ^<^(echo 'source $HOME/.bashrc; ls; pwd'^)nên chạy từ dấu nhắc lệnh của windows.
user2561747

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


39

Đây là một câu trả lời muộn, nhưng tôi đã gặp chính xác vấn đề tương tự và Google đã gửi cho tôi đến trang này, vì vậy, đây là cách tôi giải quyết vấn đề.

Theo như tôi có thể nói, bashkhông có tùy chọn để làm những gì người đăng ban đầu muốn làm. Các -ctùy chọn sẽ luôn luôn trở lại sau khi các lệnh đã được thực hiện.

Giải pháp bị hỏng : Cố gắng đơn giản và rõ ràng để giải quyết vấn đề này là:

bash -c 'XXXX ; bash'

Điều này một phần hoạt động (mặc dù có thêm một lớp vỏ phụ). Tuy nhiên, vấn đề là trong khi trình bao con sẽ kế thừa các biến môi trường đã xuất, các bí danh và hàm không được kế thừa. Vì vậy, điều này có thể hiệu quả đối với một số thứ nhưng không phải là giải pháp chung.

Tốt hơn : Cách giải quyết vấn đề này là tạo động một tệp khởi động và gọi bash bằng tệp khởi tạo mới này, đảm bảo rằng tệp init mới của bạn gọi tệp thường xuyên ~/.bashrcnếu cần.

# 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

Điều thú vị là tệp init tạm thời sẽ tự xóa ngay khi nó được sử dụng, giảm nguy cơ nó không được dọn dẹp đúng cách.

Lưu ý: Tôi không chắc liệu / etc / bashrc có thường được gọi như một phần của trình bao không đăng nhập bình thường hay không. Nếu vậy, bạn có thể muốn nguồn / etc / bashrc cũng như của bạn ~/.bashrc.


Các rm -f $TMPFILEđiều làm cho nó. Tôi thực sự không nhớ trường hợp sử dụng mà tôi đã có cho điều này, và bây giờ tôi sử dụng các kỹ thuật tự động hóa tiên tiến hơn, nhưng đây là cách để đi!
Félix Saparelli

7
Không file tmp với thay thế tiến trìnhbash --rcfile <(echo ". ~/.bashrc; a=b")
Ciro Santilli郝海东冠状病六四事件法轮功

3
Tệp tạm thời sẽ chỉ được làm sạch nếu tập lệnh Bash của bạn chạy thành công. Một giải pháp mạnh mẽ hơn sẽ được sử dụng trap.
tripleee

5

Bạn có thể chuyển --rcfilecho Bash để làm cho nó đọc một tệp bạn chọn. Tệp này sẽ được đọc thay vì của bạn .bashrc. (Nếu đó là sự cố, hãy lấy nguồn ~/.bashrctừ tập lệnh khác.)

Chỉnh sửa: Vì vậy, một hàm để bắt đầu một trình bao mới với nội dung từ ~/.more.shđó sẽ trông giống như sau:

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

... và trong .more.shbạn sẽ có các lệnh bạn muốn thực hiện khi trình bao khởi động. (Tôi cho rằng sẽ rất thanh lịch nếu tránh một tệp khởi động riêng biệt - bạn không thể sử dụng đầu vào tiêu chuẩn vì khi đó trình bao sẽ không tương tác, nhưng bạn có thể tạo tệp khởi động từ tài liệu tại đây ở một vị trí tạm thời, sau đó đọc nó.)


3

Bạn có thể có được chức năng bạn muốn bằng cách tìm nguồn cung cấp tập lệnh thay vì chạy nó. ví dụ:

tập lệnh $ cat
cmd1
cmd2
$. kịch bản
$ tại thời điểm này cmd1 và cmd2 đã được chạy bên trong shell này

Hừ! Điều đó hoạt động, nhưng không có cách nào để nhúng mọi thứ vào một alias=''?
Félix Saparelli,

1
Không bao giờ sử dụng bí danh; sử dụng các chức năng thay thế. Bạn có thể đặt: 'foo () {cmd1; cmd2; } 'bên trong tập lệnh khởi động của bạn, và sau đó thực thi foo sẽ chạy hai lệnh trong trình bao hiện tại. Lưu ý rằng không có giải pháp cung cấp cho bạn một lớp vỏ mới, nhưng bạn có thể làm 'bash exec' rồi 'foo'
William Pursell

1
Không bao giờ nói không bao giờ. bí danh có vị trí của mình
glenn jackman

Có một ví dụ về trường hợp sử dụng trong đó một hàm không thể được sử dụng thay cho bí danh không?
William Pursell,

3

Nối vào ~/.bashrcmột phần như thế này:

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

Sau đó, bạn có thể bắt đầu vỏ con với sub.


Điểm cho sự độc đáo. Bản thân bí danh có lẽ tốt hơn env subshell=true bash(vì điều đó sẽ không bị rò rỉ $subshellcho các lệnh tiếp theo): Sử dụng env so với sử dụng export .
Félix Saparelli

Bạn đúng rồi. Nhưng tôi nhận thấy rằng nó hoạt động mà không có env. Để kiểm tra xem có được xác định hay không, tôi sử dụng echo $subshell(thay vì env | grep subshellbạn sử dụng).
dashohoxha

3

Câu trả lời được chấp nhận là thực sự hữu ích! Chỉ để thêm thay thế quy trình (ví dụ <(COMMAND):) không được hỗ trợ trong một số trình bao (ví dụ dash:).

Trong trường hợp của tôi, tôi đang cố gắng tạo một hành động tùy chỉnh (về cơ bản là tập lệnh shell một dòng) trong trình quản lý tệp Thunar để bắt đầu một trình bao và kích hoạt môi trường ảo Python đã chọn. Nỗ lực đầu tiên của tôi là:

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

đâu %flà đường dẫn đến môi trường ảo do Thunar xử lý. Tôi gặp lỗi (do chạy Thunar từ dòng lệnh):

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

Sau đó, tôi nhận ra rằng sh(về cơ bản dash) của tôi không hỗ trợ thay thế quy trình.

Giải pháp của tôi là gọi bashở cấp cao nhất để diễn giải quá trình thay thế, với chi phí là một cấp bổ sung của trình bao:

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

Ngoài ra, tôi đã cố gắng sử dụng here-document cho dashnhưng không thành công. Cái gì đó như:

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

Tái bút: Tôi không có đủ danh tiếng để đăng nhận xét, người điều hành vui lòng chuyển nó sang phần nhận xét hoặc xóa nó nếu không hữu ích với câu hỏi này.


Nếu một người không muốn bash ở trên cùng (hoặc không có nó), có nhiều cách khác nhau đã được tài liệu hóa để thực hiện thay thế quy trình trực tiếp, chủ yếu là với năm mươi.
Félix Saparelli

2

Theo câu trả lời của daveraja , đây là một tập lệnh bash sẽ giải quyết được mục đích.

Hãy xem xét một tình huống nếu bạn đang sử dụng C-shell và bạn muốn thực thi một lệnh mà không cần rời khỏi ngữ cảnh / cửa sổ C-shell như sau:

Lệnh sẽ được thực thi : Tìm kiếm chính xác từ 'Kiểm tra' trong thư mục hiện tại một cách đệ quy chỉ trong các tệp * .h, * .c

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

Giải pháp 1 : Nhập vào bash từ C-shell và thực hiện lệnh

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

Giải pháp 2 : Viết lệnh dự định vào một tệp văn bản và thực thi nó bằng cách sử dụng bash

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

Giải pháp 3 : Chạy lệnh trên cùng một dòng bằng cách sử dụng bash

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

Giải pháp 4 : Tạo một sciprt (một lần) và sử dụng nó cho tất cả các lệnh trong tương lai

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

Kịch bản như sau,

#!/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
Đây là một câu trả lời hoàn toàn khác cho một vấn đề hoàn toàn khác. (Thật tuyệt khi bạn đã tìm ra điều đó, nhưng nó không thuộc chủ đề này. Nếu điều này không tồn tại trên trang web này, hãy xem xét mở một câu hỏi mới bằng câu thứ hai của bạn, sau đó ngay lập tức tự trả lời câu hỏi còn lại ... đó là cách điều này nên được xử lý ☺) Cũng xem câu trả lời của Ciro Santilli để biết cách thực hiện mà không cần xây dựng tệp tạm thời.
Félix Saparelli

Được gắn cờ không phải là câu trả lời, vì nó không phải là một nỗ lực để trả lời câu hỏi này (mặc dù đó là một nỗ lực thực sự tuyệt vời để trả lời một câu hỏi khác!)
Charles Duffy

0

Đây là một biến thể (đang hoạt động) khác:

Thao tác này sẽ mở ra một thiết bị đầu cuối gnome mới, sau đó trong thiết bị đầu cuối mới, nó chạy bash. Tệp rc của người dùng được đọc trước tiên, sau đó một lệnh ls -lađược gửi để thực thi đến trình bao mới trước khi nó chuyển sang tương tác. Tiếng vọng cuối cùng thêm một dòng mới cần thiết để kết thúc quá trình thực thi.

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

Đôi khi tôi cũng thấy hữu ích khi trang trí thiết bị đầu cuối, ví dụ như với màu sắc để định hướng tốt hơn.

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

-1

Thực thi các lệnh trong trình bao nền

Chỉ cần thêm &vào cuối lệnh, ví dụ:

bash -c some_command && another_command &

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

Trong trường hợp bạn không thể hoặc không muốn sử dụng thay thế quy trình:

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

Cách khác:

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

sh-chỉ cách ( dash, busybox):

$ ENV=script sh

Các lựa chọn thay thế tốt, nhưng sẽ có lợi nếu phần trên cùng (trước ENV=scriptphần) bị xóa hoặc đổi từ khóa để tránh bị nhầm lẫn với bản sao chép trắng trợn của câu trả lời trên cùng.
Félix Saparelli

@ FélixSaparelli Thành thật mà nói, tất cả các giải pháp nhưng dấu ENV+ shđều có trong các câu trả lời khác, nhưng chủ yếu bị chôn vùi trong hàng tấn văn bản hoặc được bao quanh bởi mã không cần thiết / không cần thiết. Tôi tin rằng có một bản tóm tắt ngắn gọn, rõ ràng sẽ có lợi cho mọi người.
x-yuri
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.