คำตอบ:
นี่เป็นการตรวจสอบว่าเชลล์เป็นแบบโต้ตอบหรือไม่ ในกรณีนี้เฉพาะการจัดหา~/.bash_profile
ไฟล์ถ้าเชลล์เป็นแบบโต้ตอบ
ดู"นี่คือเชลล์แบบโต้ตอบหรือไม่" ใน bash manual ซึ่งอ้างถึงสำนวนเฉพาะนั้น (แนะนำให้ตรวจสอบว่าเชลล์เป็นแบบโต้ตอบหรือไม่โดยการทดสอบว่า$-
ตัวแปรพิเศษมีi
อักขระหรือไม่ซึ่งเป็นแนวทางที่ดีกว่าสำหรับปัญหานี้)
bash
ยกเลิก PS1 เมื่อไม่มีการโต้ตอบ (การพิมพ์ผิดในความคิดเห็นก่อนหน้าของคุณ) เป็นข้อผิดพลาด IMO, PS1 ไม่ได้เป็นตัวแปรเฉพาะของ bash แต่ก็ไม่มีการตั้งค่าธุรกิจ มันเป็นเชลล์ตัวเดียวที่ทำเช่นนั้น (แม้ว่าจะyash
ตั้งPS1
เป็นค่าเริ่มต้นแม้ว่าจะไม่ใช่แบบโต้ตอบ)
[[ $- = *i* ]] && source ~/.bash_profile
)
[ -n "${PS1}" ]
แต่ฉันยังคงปรับปรุงคำตอบของฉันเพื่อเน้นว่าคู่มือทุบตียังแนะนำ / แนะนำการตรวจสอบ$-
เพื่อตรวจสอบว่าเปลือกเป็นแบบโต้ตอบฉันหวังว่าคุณจะพบว่าปรับปรุงคำตอบ ไชโย!
นี่เป็นวิธีการทดสอบอย่างกว้างขวางว่าเชลล์เป็นแบบโต้ตอบหรือไม่ ระวังว่าใช้งานได้กับ bash เท่านั้นมันไม่ทำงานกับ shell อื่น ๆ ดังนั้นมันก็โอเค (ถ้าโง่) สำหรับ.bashrc
แต่มันจะไม่ทำงาน.profile
(ซึ่งอ่านโดย sh และ bash เป็นหนึ่งในการนำไปใช้ที่เป็นไปได้ของ sh และไม่ใช่สิ่งที่พบได้บ่อยที่สุด)
เชลล์แบบโต้ตอบตั้งค่าตัวแปรเชลล์PS1
เป็นสตริงพร้อมต์เริ่มต้น ดังนั้นหากเชลล์เป็นแบบโต้ตอบPS1
จะมีการตั้งค่าไว้ (ยกเว้นกรณีที่ผู้ใช้.bashrc
ลบออกซึ่งยังไม่เกิดขึ้นที่ด้านบนของ.bashrc
และคุณอาจพิจารณาว่าเป็นเรื่องโง่ที่ต้องทำต่อไป)
การสนทนาเป็นจริงใน bash: อินสแตนซ์ที่ไม่โต้ตอบของ bash unset PS1
เมื่อเริ่มทำงาน โปรดทราบว่าพฤติกรรมนี้มีลักษณะเฉพาะสำหรับการทุบตีและเป็นจุดบกพร่อง (ทำไมจะbash -c '… do stuff with $var…'
ไม่ทำงานเมื่อvar
เป็นเช่นนั้นPS1
) แต่ทุกรุ่นทุบตีถึงและรวม 4.4 (รุ่นล่าสุดที่ฉันเขียน) ทำเช่นนี้
ระบบจำนวนมากส่งออกPS1
ไปยังสภาพแวดล้อม มันเป็นความคิดที่ไม่ดีเพราะเชลล์ที่ต่างกันใช้PS1
แต่มีรูปแบบที่แตกต่างกัน (เช่นการพร็อมต์ของ bashนั้นแตกต่างอย่างสิ้นเชิงจากการพร็อพเพอร์ตี้ของ zsh ) แต่มันมีมากพอที่ในทางปฏิบัติการเห็นว่าPS1
ชุดนั้นไม่ใช่ตัวบ่งชี้ที่เชื่อถือได้ว่าเชลล์นั้นเป็นแบบโต้ตอบ เชลล์อาจสืบทอดมาPS1
จากสภาพแวดล้อม
.bashrc
เป็นไฟล์ที่ทุบตีอ่านเมื่อเริ่มต้นเมื่อมันเป็นแบบโต้ตอบ ความจริงที่ไม่ค่อยเป็นที่รู้จักคือ bash อ่าน.bashrc
เป็นเชลล์ล็อกอินและฮิวริสติกของ bash สรุปได้ว่านี่เป็นเซสชันระยะไกล (bash ตรวจสอบว่าพาเรนต์เป็นrshd
หรือsshd
) ในกรณีที่สองนี้ไม่น่าเป็นไปได้ที่PS1
จะถูกตั้งค่าในสภาพแวดล้อมเนื่องจากยังไม่มีไฟล์ dot ที่ทำงาน
อย่างไรก็ตามวิธีการที่รหัสใช้ข้อมูลนี้คือการต่อต้าน
.bash_profile
ในเชลล์นั้น แต่.bash_profile
เป็นสคริปต์เวลาเข้าสู่ระบบ อาจเรียกใช้บางโปรแกรมที่มีวัตถุประสงค์เพื่อให้ทำงานเพียงครั้งเดียวต่อเซสชัน อาจแทนที่ตัวแปรสภาพแวดล้อมบางอย่างที่ผู้ใช้ตั้งค่าโดยเจตนาเป็นค่าอื่นก่อนที่จะรันเชลล์นั้น การทำงาน.bash_profile
ในเชลล์ที่ไม่ใช่การเข้าสู่ระบบนั้นก่อกวน.bash_profile
ถ้าเปลือกเป็นเปลือกเข้าสู่ระบบจากระยะไกลที่ไม่ใช่แบบโต้ตอบก็จะไม่โหลด แต่เป็นกรณีที่โหลด.bash_profile
อาจจะมีประโยชน์เพราะเปลือกเข้าสู่ระบบไม่โต้ตอบไม่ได้โหลดโดยอัตโนมัติและ/etc/profile
~/.profile
ผมคิดว่าคนเหตุผลที่ทำเช่นนี้สำหรับผู้ใช้ที่เข้าสู่ระบบผ่าน GUI (กรณีบ่อยมาก) และที่ใส่การตั้งค่าตัวแปรสภาพแวดล้อมของพวกเขาในมากกว่า.bash_profile
.profile
กลไกการเข้าสู่ระบบ GUI ส่วนใหญ่จะเรียกใช้.profile
แต่ไม่ใช่.bash_profile
(การอ่าน.bash_profile
จะต้องใช้การทุบตีเป็นส่วนหนึ่งของการเริ่มต้นเซสชันแทนการดวลจุดโทษ) ด้วยการกำหนดค่านี้เมื่อผู้ใช้เปิดเทอร์มินัลพวกเขาจะได้รับตัวแปรสภาพแวดล้อม อย่างไรก็ตามผู้ใช้จะไม่ได้รับตัวแปรสภาพแวดล้อมในแอปพลิเคชัน GUI ซึ่งเป็นแหล่งของความสับสนที่พบบ่อยมาก ทางออกที่นี่คือการใช้.profile
แทน.bash_profile
การตั้งค่าตัวแปรสภาพแวดล้อม การเพิ่มบริดจ์ระหว่าง.bashrc
และ.bash_profile
สร้างปัญหามากกว่าที่จะแก้
มีวิธีการทดสอบแบบพกพาที่ตรงไปตรงมาว่าเชลล์ปัจจุบันเป็นแบบโต้ตอบหรือไม่: ทดสอบว่า-i
เปิดใช้งานตัวเลือกหรือไม่
case $- in
*i*) echo "This shell is interactive";;
*) echo "This shell is not interactive";;
esac
สิ่งนี้มีประโยชน์ใน.bashrc
การอ่าน.profile
เฉพาะกรณีที่เชลล์ไม่มีการโต้ตอบ - นั่นคือสิ่งที่ตรงกันข้ามกับที่โค้ดทำ! อ่าน.profile
ว่าทุบตีเป็นเปลือกเข้าสู่ระบบ (ไม่โต้ตอบ) และไม่อ่านถ้ามันเป็นเปลือกโต้ตอบ
if [[ $- != *i* && -r ~/.profile ]]; then . ~/.profile; fi
[[ -o interactive ]]
(ksh, bash, zsh) หรือcase $- in (*i*) ...; esac
(POSIX)
PS1
ไม่ได้รับการตั้งค่าหากไม่ได้ทำงานแบบโต้ตอบ มันง่ายพอที่จะทดสอบ: PS1=cuckoo bash -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'
จะไม่พิมพ์อะไรเลยในขณะที่PS1=cuckoo bash -i -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'
พิมพ์ค่าที่$PS1
ตั้งไว้ในไฟล์ bash startup ของคุณ (มันจะไม่พิมพ์สตริง "cuckoo")
$-
มีi
สิ่งนั้นในเชลล์แบบโต้ตอบ
[ -n "${PS1}" ]
ผิดจะไปไกลเกินไปหลังจากทั้งหมดจะหยุดก็ต่อเมื่อมีคนส่งออก PS1 (ซึ่งในคำตอบของคุณคุณบอกว่ามันเป็นความคิดที่ไม่ดีและแม้แต่ไปสู่เหตุผลว่าทำไม) bash anyways (เพราะมันไม่รองรับ PS1 และ PS2 ถ้า shell นั้นไม่สามารถโต้ตอบได้) บางทีอาจใช้คำเช่น "discouraged" หรือพูดถึง "ข้อ จำกัด " ของวิธีการที่ดีกว่า ฉันไม่คิดว่ามันเป็น "ผิด" ทั้งหมด หากมีสิ่งใดผิดพลาดกำลังส่งออก PS1 นั่นแน่นอน! อย่างไรก็ตามขอขอบคุณสำหรับรายละเอียดของสิ่งนี้
ดูเหมือนว่าแนวคิดที่แปลกประหลาดนี้เป็นผลมาจากความจริงที่bash
ไม่ได้เริ่มเป็น POSIX เชลล์โคลน แต่เป็นBourne Shell
โคลน
เป็นผลให้เพิ่มลักษณะการทำงานแบบโต้ตอบ POSIX ( $ENV
เรียกว่าเชลล์แบบโต้ตอบ) ในภายหลังbash
และไม่เป็นที่รู้จักอย่างกว้างขวาง
มีหนึ่งเชลล์ที่ให้พฤติกรรมแบบเดียวกัน นี่คือcsh
และทุน csh ที่$prompt
มีค่าเฉพาะ:
$prompt not set non-interactive shell, test $?prompt.
$prompt set but == "" .cshrc called by the which(1) command.
$prompt set and != "" normal interactive shell.
แต่สิ่งนี้ไม่ได้ใช้กับ Bourne Shell หรือ POSIX shells
สำหรับ POSIX เชลล์วิธีเดียวที่ได้รับคือใส่รหัสสำหรับเชลล์แบบโต้ตอบลงในไฟล์:
$ENV
ที่มีชื่อเฉพาะของเชลล์ มันเป็นเช่น
$HOME/.kshrc for the korn shell
$HOME/.bashrc for bash
$HOME/.mkshrc for mksh
$HOME/.shrc for the POSIX Bourne Shell
คนอื่น ๆ พูดถึงเชลล์แฟล็-i
ก แต่นี่ไม่สามารถใช้กับการเขียนโปรแกรมที่เชื่อถือได้ POSIX ไม่ต้องการให้set -i
ผลงานหรือที่$-
มีi
เปลือกหอยแบบโต้ตอบ POSIX ต้องการเพียงแค่sh -i
บังคับให้เชลล์เข้าสู่โหมดการโต้ตอบ
เนื่องจากตัวแปร$PS1
สามารถนำเข้าจากสภาพแวดล้อมมันอาจมีค่าแม้ในโหมดที่ไม่โต้ตอบ ความจริงที่ว่าbash
unset
s PS1
ในเปลือกไม่โต้ตอบไม่ได้รับมาตรฐานและไม่ได้ทำโดยเปลือกอื่น ๆ
ดังนั้นการเขียนโปรแกรมทำความสะอาด (แม้จะมีbash
) $HOME/.bashrc
คือการใส่คำสั่งสำหรับเปลือกหอยโต้ตอบเข้า
ฉันจะพูดถึงสิ่งที่เดเบียนเป็นอันดับแรกและส่วนใหญ่แล้วเวลาที่อูบุนตูใช้เพื่อทุบตี และสัมผัสหลังในระบบอื่น ๆ
ในการตั้งค่าไฟล์เชลล์เริ่มมีความเห็นมากมาย
ฉันมีความคิดเห็นด้วย แต่ฉันจะพยายามแสดงตัวอย่างการตั้งค่าที่ถูกต้อง
ฉันจะใช้ debuan เพราะมันค่อนข้างง่ายในการค้นหาตัวอย่างของไฟล์
และเดเบียนถูกใช้อย่างหนักดังนั้นการตั้งค่าจึงผ่านการทดสอบอย่างดี
เพื่อค้นหาว่าเชลล์นั้นเป็นแบบโต้ตอบหรือไม่
เริ่มต้น/etc/profile
ใน Debianและ Ubuntu (จาก / usr / share / ฐาน / ไฟล์รายละเอียด):
if [ "${PS1-}" ]; then
if [ "${BASH-}" ] && [ "$BASH" != "/bin/sh" ]; then
การอ่านถ้า: ถ้าโต้ตอบ (ชุดเริ่มต้น PS1) และมันเป็นเปลือกทุบตี (แต่ไม่ได้ทำหน้าที่เป็นค่าเริ่มต้นsh
) แล้วเปลี่ยน PS1 เป็นหนึ่งใหม่โดยเฉพาะ (ไม่ใช่ค่าเริ่มต้น)
เริ่มต้น/etc/bash.bashrc
ในเดเบียนยังมี:
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
ซึ่งค่อนข้างชัดเจนในสิ่งที่มันทำ: หากการโต้ตอบไม่ได้มา (ส่วนที่เหลือ)
อย่างไรก็ตามใน/etc/skel/.bashrc
เป็นตัวอย่างของวิธีที่ถูกต้องในการทดสอบเชลล์แบบโต้ตอบ (โดยใช้$-
):
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
นั่นควรแสดงให้เห็นอย่างชัดเจนว่าทำไม PS1 และอีกทางเลือกหนึ่ง
ควรหลีกเลี่ยงการตั้งค่าที่คุณรายงาน
ลำดับ (จากการตั้งค่าระบบการตั้งค่าผู้ใช้เฉพาะเจาะจงมากขึ้น (สำหรับทุบตี)) เป็น/etc/profile
, /etc/bash.bashrc
, และในที่สุด~/.profile
~/.bashrc
ที่วางเอฟเฟกต์ที่กว้างที่สุด (และสำหรับเชลล์เพิ่มเติม) ใน/etc/profile
(ซึ่งเป็นเจ้าของโดยรูท) ตามด้วย/etc/bash.bashrc
(ซึ่งเป็นเจ้าของโดยรูท) แต่มีผลกับการทุบตีเท่านั้น จากนั้นมาตั้งค่าส่วนตัวใน$HOME
ครั้งแรก~/.profile
สำหรับเชลล์ส่วนใหญ่และ~/.bashrc
(เกือบเทียบเท่า~/.bash_profile
) เฉพาะสำหรับทุบตีเท่านั้น
ดังนั้นจึงเป็นเรื่องที่ไม่ถูกต้องไปยังแหล่ง~/.bashrc
ใน~/.profile
ก็จะเปลี่ยนผู้ใช้เฉพาะการตั้งค่าสำหรับการทุบตีทั่วไปอื่น ๆ ที่มีผลกระทบต่อหอยมากขึ้น ยกเว้นถ้าทำด้วยวิธีนี้ :
# ~/.profile: executed by the command interpreter for login shells
# if running bash
if [ -n "$BASH_VERSION" ]; then
# include .bashrc if it exists
if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi
fi
มันตรวจสอบว่าทุบตีทำงานและโหลดเฉพาะ.bashrc
ในกรณีที่
นี่เป็นการตัดสินใจขั้นต้นจากเดเบียน เหตุผลที่จะมีการอธิบายที่นี่
ในความเป็นจริงการย้อนกลับการจัดหา~/.profile
ใน~/.bash_profile
(หรือ~/.bashrc
) เป็นเพียงการใช้กฎทั่วไปอีกครั้งที่ควรได้รับการโหลดไปยังกรณีการใช้งานเฉพาะแล้วดังนั้น "ไม่เลว" (ฉันไม่ได้พูดว่า "ดี") และฉันไม่ได้บอกว่าดีเพราะอาจทำให้การจัดหาไฟล์วนซ้ำ เช่นเดียวกับเมื่อไดเรกทอรีย่อยโหลดพาเรนต์นั่นคือลูปไดเรกทอรี
และในการจัดหาข้ามที่ตรวจสอบเปลือกโต้ตอบทำให้รู้สึก เมื่อ~/.bashrc
โหลดเชลล์แบบอินเทอร์แอคทีฟเท่านั้น แต่อาจจะโหลด~/.profile
(หรือวิธีอื่น ๆ ) และในกรณีนี้การตรวจสอบเชลล์แบบอินเทอร์แอคทีฟก็สามารถใช้ได้
( export PS1='abc$ '; bash -c 'echo "[$PS1]"' )
[]
ดูเหมือนว่า zsh จะไม่ทำอย่างเดียวกันอย่างน้อยจากการทดสอบ ... ไม่ว่าในกรณีใดเจตนาของการ[ -n "$PS1" ]
ตรวจสอบว่าเชลล์นั้นเป็นแบบโต้ตอบหรือไม่