วิธีเรียกใช้ bash รันคำสั่งภายในเชลล์ใหม่จากนั้นให้การควบคุมกลับไปยังผู้ใช้


86

สิ่งนี้ต้องเรียบง่ายหรือซับซ้อนจริงๆ แต่ฉันไม่พบอะไรเลย ... ฉันกำลังพยายามเปิดอินสแตนซ์ bash ใหม่จากนั้นเรียกใช้คำสั่งสองสามคำภายในและให้การควบคุมกลับไปยังผู้ใช้ภายในนั้น เช่นเดียวกัน

ฉันเหนื่อย:

$ bash -lic "some_command"

แต่จะดำเนินการsome_commandภายในอินสแตนซ์ใหม่จากนั้นจึงปิด ฉันต้องการให้มันเปิดอยู่

อีกหนึ่งรายละเอียดที่อาจส่งผลต่อคำตอบ: หากฉันสามารถใช้งานได้ฉันจะใช้ใน.bashrcนามแฝงของฉันดังนั้นคะแนนโบนัสสำหรับaliasการนำไปใช้งาน!


ฉันไม่เข้าใจส่วนสุดท้ายเกี่ยวกับนามแฝง นามแฝงดำเนินการในบริบทของเชลล์ปัจจุบันดังนั้นจึงไม่มีปัญหาที่คุณขอให้แก้ไข (แม้ว่าโดยปกติแล้วฟังก์ชันจะดีกว่านามแฝงด้วยเหตุผลหลายประการ)
tripleee

2
ฉันต้องการใส่บางอย่างเช่นalias shortcut='bash -lic "some_command"'ใน. bashrc ของฉันจากนั้นเรียกใช้shortcutและมีเชลล์ใหม่ที่some_commandรันอยู่
Félix Saparelli

คำตอบ:


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

แจกจ่ายการสร้างไฟล์ชั่วคราว คำถามเกี่ยวกับเว็บไซต์อื่น ๆ :


ฉันพยายามทำสิ่งนี้ใน Windows 10 (พยายามเขียนชุดไฟล์ / สคริปต์ "เริ่มต้น") และได้รับThe system cannot find the file specified.
Doctor Blue

1
@Scott ดีบักทีละขั้นตอน: 1) cat ~/.bashrcทำงานหรือไม่ 2) ได้cat <(echo a)ผลหรือไม่? 3) ได้bash --rcfile somefileผลหรือไม่?
Ciro Santilli 郝海东冠状病六四事件法轮功

1
@Scott เราเพิ่งพบปัญหานี้ใน WSL (เรียกใช้ bash จากไฟล์แบตช์เริ่มต้น) วิธีแก้ปัญหาของเราคือการหลีกเลี่ยงอักขระบางตัวเพื่อให้แบทช์เข้าใจ: bash.exe --rcfile ^<^(echo 'source $HOME/.bashrc; ls; pwd'^)ควรเรียกใช้จากพรอมต์คำสั่งของ windows
user2561747

มีวิธีทำให้ใช้งานได้กับการสลับผู้ใช้เช่นsudo bash --init-file <(echo "ls; pwd")หรือsudo -iu username bash --init-file <(echo "ls; pwd")?
jeremysprofile


40

นี่เป็นคำตอบที่ล่าช้า แต่ฉันมีปัญหาเดียวกันและ Google ก็ส่งฉันไปที่หน้านี้ดังนั้นเพื่อความสมบูรณ์นี่คือวิธีที่ฉันแก้ไขปัญหา

เท่าที่ฉันสามารถบอกได้ว่าbashไม่มีตัวเลือกในการทำสิ่งที่ผู้โพสต์ต้นฉบับต้องการทำ -cตัวเลือกที่จะกลับมาหลังจากคำสั่งที่ได้รับการดำเนินการ

วิธีแก้ปัญหาที่เสียหาย : ความพยายามที่ง่ายที่สุดและชัดเจนในเรื่องนี้คือ:

bash -c 'XXXX ; bash'

บางส่วนใช้งานได้ (แม้ว่าจะมีเลเยอร์เปลือกย่อยพิเศษ) อย่างไรก็ตามปัญหาคือแม้ว่า sub-shell จะสืบทอดตัวแปรสภาพแวดล้อมที่เอ็กซ์พอร์ตนามแฝงและฟังก์ชันจะไม่ได้รับการสืบทอด สิ่งนี้อาจใช้ได้กับบางสิ่ง แต่ไม่ใช่วิธีแก้ปัญหาทั่วไป

ดีกว่า : วิธีแก้ปัญหานี้คือสร้างไฟล์เริ่มต้นแบบไดนามิกและเรียกใช้ bash ด้วยไฟล์เริ่มต้นใหม่นี้ตรวจสอบให้แน่ใจว่าไฟล์ init ใหม่ของคุณเรียกใช้งานปกติ~/.bashrcหากจำเป็น

# 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

สิ่งที่ดีคือไฟล์ init ชั่วคราวจะลบตัวเองทันทีที่ใช้งานช่วยลดความเสี่ยงที่ไฟล์จะไม่ถูกล้างอย่างถูกต้อง

หมายเหตุ: ฉันไม่แน่ใจว่าปกติจะเรียก / etc / bashrc เป็นส่วนหนึ่งของเชลล์ที่ไม่ใช่ล็อกอินปกติหรือไม่ ในกรณีนี้คุณอาจต้องการแหล่งที่มา / etc / bashrc เช่นเดียวกับ~/.bashrcไฟล์.


rm -f $TMPFILEสิ่งที่ทำให้มัน ฉันจำกรณีการใช้งานที่ฉันมีสำหรับสิ่งนี้ไม่ได้จริงๆและตอนนี้ฉันใช้เทคนิคการทำงานอัตโนมัติขั้นสูงมากขึ้น แต่นี่คือวิธีที่จะไป!
Félix Saparelli

8
ไม่มีไฟล์ tmp ที่มีการทดแทนกระบวนการbash --rcfile <(echo ". ~/.bashrc; a=b")
Ciro Santilli 郝海东冠状病六四事件法轮功

3
ไฟล์ temp จะถูกล้างก็ต่อเมื่อสคริปต์ Bash ของคุณทำงานสำเร็จ trapวิธีการแก้ปัญหามีประสิทธิภาพมากขึ้นจะใช้
tripleee

5

คุณสามารถส่ง--rcfileไปที่ Bash เพื่อให้มันอ่านไฟล์ที่คุณเลือกได้ ไฟล์นี้จะถูกอ่านแทนไฟล์.bashrc. (หากนั่นเป็นปัญหาที่มา~/.bashrcจากสคริปต์อื่น)

แก้ไข:ดังนั้นฟังก์ชั่นในการเริ่มเชลล์ใหม่ด้วยสิ่งต่างๆ~/.more.shจะมีลักษณะดังนี้:

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

... และในตัว.more.shคุณจะมีคำสั่งที่คุณต้องการดำเนินการเมื่อเชลล์เริ่มทำงาน (ฉันคิดว่ามันเป็นการดีที่จะหลีกเลี่ยงไฟล์เริ่มต้นแยกต่างหาก - คุณไม่สามารถใช้อินพุตมาตรฐานได้เนื่องจากเชลล์จะไม่โต้ตอบ แต่คุณสามารถสร้างไฟล์เริ่มต้นจากเอกสารที่นี่ในตำแหน่งชั่วคราวจากนั้นอ่านได้)


3

คุณสามารถรับฟังก์ชันที่คุณต้องการได้โดยการจัดหาสคริปต์แทนที่จะเรียกใช้งาน เช่น:

สคริปต์ $ cat
cmd1
cmd2
$. สคริปต์
$ ณ จุดนี้ cmd1 และ cmd2 ถูกรันภายในเชลล์นี้

อืม. มันใช้งานได้ แต่ไม่มีทางฝังทุกอย่างไว้ใน an alias=''?
Félix Saparelli

1
ห้ามใช้นามแฝง ใช้ฟังก์ชันแทน คุณสามารถใส่: 'foo () {cmd1; cmd2; } 'ภายในสคริปต์เริ่มต้นของคุณจากนั้นการเรียกใช้ foo จะเรียกใช้คำสั่งสองคำสั่งในเชลล์ปัจจุบัน โปรดทราบว่าโซลูชันเหล่านี้ไม่ได้ให้เชลล์ใหม่แก่คุณ แต่คุณสามารถทำ 'exec bash' และ 'foo' ได้
William Pursell

1
อย่าพูดไม่เคย นามแฝงมีที่มา
glenn jackman

มีตัวอย่างกรณีการใช้งานที่ไม่สามารถใช้ฟังก์ชันแทนนามแฝงได้หรือไม่?
William Pursell

3

ต่อท้าย~/.bashrcส่วนดังนี้:

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

จากนั้นคุณสามารถเริ่มต้น subshell ด้วยsub.


คะแนนสำหรับความคิดริเริ่ม นามแฝงที่ตัวเองอาจจะดีกว่าที่เป็นอยู่env subshell=true bash(ในฐานะที่จะไม่รั่วไหล$subshellไปยังคำสั่งที่ตามมา): ใช้ envเทียบกับการใช้การส่งออก
Félix Saparelli

คุณพูดถูก แต่ฉันสังเกตเห็นว่ามันทำงานโดยไม่ได้envเช่นกัน สำหรับการตรวจสอบว่ามีการกำหนดไว้หรือไม่ฉันใช้echo $subshell(แทนที่env | grep subshellคุณใช้)
dashohoxha

3

คำตอบที่ได้รับการยอมรับนั้นมีประโยชน์จริงๆ! เพียงเพื่อเพิ่มการทดแทนกระบวนการนั้น (เช่น<(COMMAND)) ไม่ได้รับการสนับสนุนในบางเชลล์ (เช่นdash)

ในกรณีของฉันฉันพยายามสร้างแอ็คชันแบบกำหนดเอง (โดยทั่วไปคือเชลล์สคริปต์หนึ่งบรรทัด) ในตัวจัดการไฟล์ Thunar เพื่อเริ่มเชลล์และเปิดใช้งานสภาพแวดล้อมเสมือน Python ที่เลือก ความพยายามครั้งแรกของฉันคือ:

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

ที่%fเป็นเส้นทางไปยังสภาพแวดล้อมเสมือนการจัดการโดย Thunar ฉันได้รับข้อผิดพลาด (โดยเรียกใช้ Thunar จากบรรทัดคำสั่ง):

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

จากนั้นฉันก็ตระหนักว่าsh(โดยพื้นฐานdash) ของฉันไม่สนับสนุนการทดแทนกระบวนการ

วิธีแก้ปัญหาของฉันคือเรียกใช้bashที่ระดับบนสุดเพื่อตีความการทดแทนกระบวนการด้วยค่าใช้จ่ายของเชลล์ระดับพิเศษ:

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

หรือฉันพยายามใช้เอกสารที่นี่dashแต่ไม่ประสบความสำเร็จ สิ่งที่ต้องการ:

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

PS: ฉันไม่มีชื่อเสียงมากพอที่จะโพสต์ความคิดเห็นผู้ดูแลโปรดอย่าลังเลที่จะย้ายไปแสดงความคิดเห็นหรือลบออกหากไม่เป็นประโยชน์กับคำถามนี้


หากใครไม่ต้องการ bash อยู่ด้านบน (หรือไม่มี) มีเอกสารวิธีต่างๆในการใช้การทดแทนกระบวนการโดยตรงโดยส่วนใหญ่ใช้ fifos
Félix Saparelli

2

ตามคำตอบของdaverajaนี่คือสคริปต์ทุบตีซึ่งจะช่วยแก้วัตถุประสงค์

พิจารณาสถานการณ์หากคุณใช้C-shellและคุณต้องการดำเนินการคำสั่งโดยไม่ต้องออกจากบริบท / หน้าต่าง C-shell ดังนี้

คำสั่งที่จะดำเนินการ : ค้นหาคำว่า 'การทดสอบ' ในไดเร็กทอรีปัจจุบันแบบวนซ้ำเฉพาะในไฟล์ * .h, * .c

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

โซลูชันที่ 1 : เข้าสู่ bash จาก C-shell และดำเนินการคำสั่ง

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

โซลูชันที่ 2 : เขียนคำสั่งที่ต้องการลงในไฟล์ข้อความและดำเนินการโดยใช้ bash

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

โซลูชันที่ 3 : เรียกใช้คำสั่งในบรรทัดเดียวกันโดยใช้ bash

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

โซลูชันที่ 4 : สร้าง sciprt (ครั้งเดียว) และใช้สำหรับคำสั่งในอนาคตทั้งหมด

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

สคริปต์มีดังนี้

#!/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
นี่เป็นคำตอบที่แตกต่างกันอย่างสิ้นเชิงสำหรับปัญหาที่แตกต่างไปจากเดิมอย่างสิ้นเชิง (เป็นเรื่องดีที่คุณคิดออก แต่มันไม่ได้อยู่ในหัวข้อนี้หากไม่มีในเว็บไซต์นี้ให้ลองเปิดคำถามใหม่ด้วยประโยคที่สองของคุณจากนั้นตอบคำถามด้วยตัวเองทันทีพร้อมกับส่วนที่เหลือ นั่นคือวิธีที่ควรจัดการ☺) ดูคำตอบของ Ciro Santilli สำหรับวิธีการทำโดยไม่ต้องสร้างไฟล์ชั่วคราวเลย
Félix Saparelli

ตั้งค่าสถานะว่าไม่ใช่คำตอบเนื่องจากไม่ใช่ความพยายามที่จะตอบคำถามนี้ (แม้ว่าจะเป็นความพยายามที่ดีมากในการตอบคำถามอื่น!)
Charles Duffy

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

ในกรณีที่คุณไม่สามารถหรือไม่ต้องการใช้การทดแทนกระบวนการ:

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

อีกวิธีหนึ่ง:

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

sh- วิธีเดียว ( dash, busybox):

$ ENV=script sh

ทางเลือกที่ดี แต่จะได้รับประโยชน์จากการเอาส่วนบนสุด (ก่อนENV=scriptส่วนนั้น) ออกหรือเปลี่ยนคำใหม่เพื่อหลีกเลี่ยงความสับสนกับการคัดลอกวางที่ชัดเจนของคำตอบด้านบน
Félix Saparelli

@ FélixSaparelliเพื่อให้ตรงไปตรงมาในการแก้ปัญหาทั้งหมด แต่มีENV+ shหนึ่งอยู่ในคำตอบอื่น ๆ แต่ส่วนใหญ่ถูกฝังอยู่ในข้อความจำนวนมากหรือล้อมรอบด้วยรหัสที่ไม่จำเป็น / ไม่จำเป็น ฉันเชื่อว่าการสรุปสั้น ๆ ที่ชัดเจนจะเป็นประโยชน์ต่อทุกคน
x-yuri

0

นี่คือตัวแปรอื่น (ที่ใช้งานได้):

สิ่งนี้จะเปิดเทอร์มินัล gnome ใหม่จากนั้นในเทอร์มินัลใหม่จะรัน bash ไฟล์ rc ของผู้ใช้จะถูกอ่านก่อนจากนั้นคำสั่งls -laจะถูกส่งไปยังเชลล์ใหม่ก่อนที่จะเปลี่ยนเป็นแบบโต้ตอบ เสียงสะท้อนสุดท้ายจะเพิ่มบรรทัดใหม่พิเศษที่จำเป็นในการดำเนินการให้เสร็จสิ้น

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

บางครั้งฉันยังพบว่ามีประโยชน์ในการตกแต่งเทอร์มินัลเช่นด้วยสีเพื่อการวางแนวที่ดีขึ้น

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

-1

ดำเนินการคำสั่งในเปลือกพื้นหลัง

เพียงเพิ่ม&ที่ส่วนท้ายของคำสั่งเช่น:

bash -c some_command && another_command &
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.