วัตถุประสงค์ของ [-n“ $ PS1”] ใน bashrc


10

วัตถุประสงค์อะไรไม่[ -n "$PS1" ]ใน[ -n "$PS1" ] && source ~/.bash_profile;บริการ? บรรทัดนี้รวมอยู่ใน.bashrcของ dotfiles repo

คำตอบ:


20

นี่เป็นการตรวจสอบว่าเชลล์เป็นแบบโต้ตอบหรือไม่ ในกรณีนี้เฉพาะการจัดหา~/.bash_profileไฟล์ถ้าเชลล์เป็นแบบโต้ตอบ

ดู"นี่คือเชลล์แบบโต้ตอบหรือไม่" ใน bash manual ซึ่งอ้างถึงสำนวนเฉพาะนั้น (แนะนำให้ตรวจสอบว่าเชลล์เป็นแบบโต้ตอบหรือไม่โดยการทดสอบว่า$-ตัวแปรพิเศษมีiอักขระหรือไม่ซึ่งเป็นแนวทางที่ดีกว่าสำหรับปัญหานี้)


ทุบตีอย่างน้อยจะล้างค่า PS1 และ PS2 ถ้าเปลือกเป็นแบบโต้ตอบ คุณจะเห็นว่าสำหรับตัวคุณเองด้วยซึ่งก็พิมพ์( export PS1='abc$ '; bash -c 'echo "[$PS1]"' ) []ดูเหมือนว่า zsh จะไม่ทำอย่างเดียวกันอย่างน้อยจากการทดสอบ ... ไม่ว่าในกรณีใดเจตนาของการ[ -n "$PS1" ]ตรวจสอบว่าเชลล์นั้นเป็นแบบโต้ตอบหรือไม่
filbranden

3
นั่นเป็นการbashยกเลิก PS1 เมื่อไม่มีการโต้ตอบ (การพิมพ์ผิดในความคิดเห็นก่อนหน้าของคุณ) เป็นข้อผิดพลาด IMO, PS1 ไม่ได้เป็นตัวแปรเฉพาะของ bash แต่ก็ไม่มีการตั้งค่าธุรกิจ มันเป็นเชลล์ตัวเดียวที่ทำเช่นนั้น (แม้ว่าจะyashตั้งPS1เป็นค่าเริ่มต้นแม้ว่าจะไม่ใช่แบบโต้ตอบ)
Stéphane Chazelas

1
เนื่องจากรหัสที่เป็นปัญหานั้นอยู่ในไฟล์เฉพาะทุบตีจึงเป็นคำตอบที่สมเหตุสมผล คำตอบอื่น ๆ ระบุถึงกรณีทั่วไปของ POSIX specs หรือ shell อื่น ๆ คุณตอบ“ สิ่งนี้มีจุดประสงค์อะไร” เจตนาในคำถามในขณะที่ออกจากส่วนที่เหลืออย่างเหมาะสม มันเป็นการดีที่จะรู้ว่าสิ่งที่ทุบตีกำลังทำอยู่และอาจเป็นวิธีที่ดีกว่าในการบรรลุเป้าหมายได้เช่นกัน
Jeff Schaller

ฉันจะพิจารณาคำตอบนี้สมบูรณ์มากขึ้นถ้ามันชี้ให้เห็นเป็นทางเลือกที่น่าเชื่อถือมากขึ้น (พูด[[ $- = *i* ]] && source ~/.bash_profile)
Charles Duffy

@CharlesDuffy ตรงไปตรงมาฉันไม่คิดว่ามีอะไรผิดปกติ[ -n "${PS1}" ]แต่ฉันยังคงปรับปรุงคำตอบของฉันเพื่อเน้นว่าคู่มือทุบตียังแนะนำ / แนะนำการตรวจสอบ$-เพื่อตรวจสอบว่าเปลือกเป็นแบบโต้ตอบฉันหวังว่าคุณจะพบว่าปรับปรุงคำตอบ ไชโย!
filbranden

19

สิ่งนี้ทำอะไร

นี่เป็นวิธีการทดสอบอย่างกว้างขวางว่าเชลล์เป็นแบบโต้ตอบหรือไม่ ระวังว่าใช้งานได้กับ 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

4
อาจเป็นที่น่าสังเกตว่าวิธีที่ดีกว่าในการทดสอบว่าเชลล์นั้นมีการโต้ตอบด้วย[[ -o interactive ]](ksh, bash, zsh) หรือcase $- in (*i*) ...; esac(POSIX)
Stéphane Chazelas

2
ทุบตีของฉัน (รุ่น 4.4.12) ดูเหมือนว่าจะPS1ไม่ได้รับการตั้งค่าหากไม่ได้ทำงานแบบโต้ตอบ มันง่ายพอที่จะทดสอบ: PS1=cuckoo bash -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'จะไม่พิมพ์อะไรเลยในขณะที่PS1=cuckoo bash -i -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'พิมพ์ค่าที่$PS1ตั้งไว้ในไฟล์ bash startup ของคุณ (มันจะไม่พิมพ์สตริง "cuckoo")
FooF

1
@ Stéphane Chazelas: POSIX ไม่จำเป็นต้อง$-มีiสิ่งนั้นในเชลล์แบบโต้ตอบ
schily

1
Bosh ทำสิ่งนี้ตั้งแต่ปี 2012 เพื่อให้ใช้งานร่วมกับ ksh ได้ มันไม่จำเป็นต้องใช้โดย POSIX จนกว่าการเปลี่ยนแปลงที่คุณพูดจะมีผล
schily

1
ตรงไปตรงมาฉันจะบอกว่าการโทร[ -n "${PS1}" ] ผิดจะไปไกลเกินไปหลังจากทั้งหมดจะหยุดก็ต่อเมื่อมีคนส่งออก PS1 (ซึ่งในคำตอบของคุณคุณบอกว่ามันเป็นความคิดที่ไม่ดีและแม้แต่ไปสู่เหตุผลว่าทำไม) bash anyways (เพราะมันไม่รองรับ PS1 และ PS2 ถ้า shell นั้นไม่สามารถโต้ตอบได้) บางทีอาจใช้คำเช่น "discouraged" หรือพูดถึง "ข้อ จำกัด " ของวิธีการที่ดีกว่า ฉันไม่คิดว่ามันเป็น "ผิด" ทั้งหมด หากมีสิ่งใดผิดพลาดกำลังส่งออก PS1 นั่นแน่นอน! อย่างไรก็ตามขอขอบคุณสำหรับรายละเอียดของสิ่งนี้
filbranden

1

ดูเหมือนว่าแนวคิดที่แปลกประหลาดนี้เป็นผลมาจากความจริงที่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 unsets PS1ในเปลือกไม่โต้ตอบไม่ได้รับมาตรฐานและไม่ได้ทำโดยเปลือกอื่น ๆ

ดังนั้นการเขียนโปรแกรมทำความสะอาด (แม้จะมีbash) $HOME/.bashrcคือการใส่คำสั่งสำหรับเปลือกหอยโต้ตอบเข้า


0

ฉันจะพูดถึงสิ่งที่เดเบียนเป็นอันดับแรกและส่วนใหญ่แล้วเวลาที่อูบุนตูใช้เพื่อทุบตี และสัมผัสหลังในระบบอื่น ๆ

ในการตั้งค่าไฟล์เชลล์เริ่มมีความเห็นมากมาย
ฉันมีความคิดเห็นด้วย แต่ฉันจะพยายามแสดงตัวอย่างการตั้งค่าที่ถูกต้อง
ฉันจะใช้ debuan เพราะมันค่อนข้างง่ายในการค้นหาตัวอย่างของไฟล์
และเดเบียนถูกใช้อย่างหนักดังนั้นการตั้งค่าจึงผ่านการทดสอบอย่างดี

เป้าหมายของการตรวจสอบว่ามีการตั้งค่า PS1 อย่างไร

เพื่อค้นหาว่าเชลล์นั้นเป็นแบบโต้ตอบหรือไม่

เริ่มต้น/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(หรือวิธีอื่น ๆ ) และในกรณีนี้การตรวจสอบเชลล์แบบอินเทอร์แอคทีฟก็สามารถใช้ได้

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