วิธีการที่สอดคล้องกับ checkbashisms เพื่อกำหนดเชลล์ปัจจุบัน


18

ในของ.profileฉันฉันใช้รหัสต่อไปนี้เพื่อให้แน่ใจว่านามแฝงที่เกี่ยวข้องกับ Bash และฟังก์ชั่นมีที่มาเฉพาะถ้าเปลือกเข้าสู่ระบบเป็น Bashจริง:

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

ขณะนี้ฉันกำลังวางไฟล์เชลล์สคริปต์และฟังก์ชั่นการทำงานภายใต้การควบคุมเวอร์ชัน ฉันยังเพิ่งเริ่มต้นขั้นตอนการลบ Bashisms สบายจากสคริปต์เปลือกที่ไม่ได้รับประโยชน์จากคุณลักษณะทุบตีเฉพาะเช่นแทนที่ด้วยfunction funcname()funcname()

สำหรับที่เก็บไฟล์เชลล์ของฉันฉันได้กำหนดค่าฮุคล่วงหน้าที่เรียกใช้checkbashismsยูทิลิตี้จากแพ็คเกจ devscriptsของ Debian ในแต่ละshไฟล์ในที่เก็บเพื่อให้แน่ใจว่าฉันไม่ได้แนะนำไวยากรณ์เฉพาะของ Bash โดยไม่ได้ตั้งใจ อย่างไรก็ตามสิ่งนี้ทำให้เกิดข้อผิดพลาดสำหรับ.profile:

possible bashism in .profile line 51 ($BASH_SOMETHING):
if [ "${BASH_VERSION:-}" ]; then

checkbashismsผมสงสัยว่าถ้ามีวิธีการตรวจสอบซึ่งเปลือกกำลังทำงานที่จะไม่เรียกเตือนใน

ฉันตรวจสอบรายการของตัวแปรที่เกี่ยวข้องกับเชลล์ที่ระบุโดย POSIX ด้วยความหวังว่าหนึ่งในนั้นสามารถใช้เพื่อแสดงเชลล์ปัจจุบัน ฉันยังได้ดูตัวแปรที่ตั้งค่าในเชลล์ Dash แบบโต้ตอบ แต่ไม่สามารถหาตัวเลือกที่เหมาะสมได้อีกครั้ง

ในขณะนี้ฉันได้แยกออก.profileจากการดำเนินการโดยcheckbashisms; มันเป็นไฟล์เล็ก ๆ ดังนั้นจึงไม่ยากที่จะตรวจสอบด้วยตนเอง อย่างไรก็ตามจากการวิจัยปัญหาฉันยังคงต้องการทราบว่ามีวิธีการที่สอดคล้องกับ POSIX เพื่อตรวจสอบว่าเปลือกใดกำลังทำงานอยู่ (หรืออย่างน้อยวิธีที่ไม่ทำให้เกิดความcheckbashismsล้มเหลว)


พื้นหลัง / ชี้แจงเพิ่มเติม

หนึ่งในเหตุผลที่ฉันวางไฟล์การกำหนดค่าเปลือกของฉันภายใต้การควบคุมเวอร์ชันคือการกำหนดค่าสภาพแวดล้อมของฉันในทุกระบบที่ฉันเข้าสู่ระบบเป็นประจำ: Cygwin, Ubuntu และ CentOS (ทั้ง 5 และ 7 โดยใช้ Active Directory สำหรับผู้ใช้ การตรวจสอบ) ฉันมักจะเข้าสู่ระบบผ่านทาง Windows X สภาพแวดล้อมเดสก์ทอป / และ SSH สำหรับโฮสต์ระยะไกล อย่างไรก็ตามฉันต้องการให้สิ่งนี้เป็นข้อพิสูจน์ในอนาคตและมีการพึ่งพาระบบและเครื่องมืออื่น ๆ น้อยที่สุดเท่าที่จะทำได้

ฉันใช้checkbashismsเป็นวิธีตรวจสุขภาพจิตที่เรียบง่ายแบบอัตโนมัติสำหรับไวยากรณ์ของไฟล์ที่เกี่ยวข้องกับเชลล์ มันไม่ได้เป็นเครื่องมือที่สมบูรณ์แบบเช่นฉันใช้โปรแกรมปะแก้ไปแล้วเพื่อไม่ให้บ่นเกี่ยวกับการใช้งานcommand -vในสคริปต์ของฉัน ในขณะที่ทำการค้นคว้าฉันได้เรียนรู้ว่าจุดประสงค์ที่แท้จริงของโปรแกรมคือเพื่อให้แน่ใจว่าสอดคล้องกับนโยบาย Debian ซึ่งตามที่ฉันเข้าใจแล้วนั้นใช้ POSIX 2004 มากกว่า 2008 (หรือการแก้ไข 2013)


สิ่งที่คุณกำลังทำอยู่นั้นเป็นไปตาม POSIX เช่นเดียวกับสิ่งที่อาจเกิดขึ้นเมื่อจุดรวมคือการทำงานแตกต่างกันในสภาพแวดล้อมที่สอดคล้องกับ POSIX เนื้อของคุณอยู่กับ checkbashisms
Gilles 'ดังนั้นหยุดความชั่วร้าย'

และโดยวิธีการที่ฉันไม่เคยใช้ checkbashisms เพื่อตรวจสอบการพกพาของการกำหนดค่าของฉัน ฉันจะตรวจสอบโดยใช้มันในระบบที่แตกต่างกัน
Gilles 'ดังนั้นหยุดความชั่วร้าย'

2
และโดยวิธีการสำหรับสิ่งที่คุณกำลังทำอะไรที่นี่เขียน.bash_profileว่าทั้งมา.profileและ .bashrc(ตามเงื่อนไข)
Gilles 'ดังนั้นหยุดความชั่วร้าย'

ขอบคุณสำหรับข้อเสนอแนะ @Gilles นั่นเป็นทางออกที่หรูหรามากสำหรับปัญหาเฉพาะ
Anthony G - ความยุติธรรมสำหรับ Monica

คำตอบ:


16

ของคุณ

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

รหัสเป็นไปตาม POSIX อย่างสมบูรณ์และวิธีที่ดีที่สุดในการตรวจสอบว่าคุณกำลังทำงานbashอยู่ แน่นอน$BASH_VERSIONตัวแปรคือ bash-specific แต่นั่นเป็นสาเหตุที่คุณใช้มันโดยเฉพาะ! เพื่อตรวจสอบว่าคุณกำลังทำงานอยู่bash!

โปรดทราบว่า$BASH_VERSIONจะได้รับการตั้งค่าได้ว่าbashจะเรียกว่าเป็นหรือbash shเมื่อคุณยืนยันว่าคุณกำลังใช้งานbashคุณสามารถใช้[ -o posix ]เป็นตัวบ่งชี้ว่าเชลล์ถูกเรียกใช้เป็นsh(แม้ว่าตัวเลือกนั้นจะถูกตั้งค่าเมื่อ POSIXLY_CORRECT อยู่ในสภาพแวดล้อมหรือbashถูกเรียกด้วย-o posixหรือด้วย SHELLOPTS = posix ในสภาพแวดล้อม แต่ในกรณีเหล่านั้นทั้งหมดbashจะทำงานเหมือนถูกเรียกว่าเป็นsh)


ตัวแปรอื่นที่คุณสามารถใช้แทน$BASH_VERSIONและนั่นcheckbashismดูเหมือนจะไม่บ่นเกี่ยวกับเว้นแต่ผ่านตัวเลือก-x $BASHนั่นก็เฉพาะเจาะจงbashเช่นกันดังนั้นคุณควรจะสามารถใช้เพื่อตรวจสอบว่าคุณกำลังทำงานอยู่bashหรือไม่


checkbashismsผมยังยืนยันว่ามันไม่ได้จริงๆใช้ที่เหมาะสมของ checkbashismsเป็นเครื่องมือที่จะช่วยให้คุณเขียนแบบพกพาshสคริปต์ (ตามshข้อกำหนดในการกำหนดนโยบายเด superset ของ POSIX) ก็ช่วยระบุไวยากรณ์ที่ไม่ได้มาตรฐานการแนะนำให้รู้จักคนเขียนสคริปต์ในระบบที่shเป็น symlink bashไป

A .profileถูกตีความโดยเชลล์ที่แตกต่างกันซึ่งส่วนใหญ่ไม่ได้เป็นไปตาม POSIX โดยทั่วไปแล้วคุณไม่ได้ใช้shเป็นเปลือกเข้าสู่ระบบของคุณ แต่เปลือกหอยชอบzsh, fishหรือbashมีคุณลักษณะแบบโต้ตอบที่สูงขึ้น

bashและzshเมื่อไม่ได้เรียกว่าเป็นshและเมื่อไฟล์เซสชั่นรายละเอียดของตน ( .bash_profile, .zprofile) จะไม่สอดคล้องกับ POSIX (โดยเฉพาะzsh) .profileแต่ยังคงอ่าน

ดังนั้นจึงไม่ใช่ POSIX ไวยากรณ์ที่คุณต้องการ.profileแต่เป็นไวยากรณ์ที่เข้ากันได้กับ POSIX (สำหรับsh) bashและzshถ้าคุณเคยใช้เชลล์เหล่านั้น (อาจเป็นบอร์นในขณะที่บอร์นเชลล์อ่าน.profileแต่ไม่พบบนระบบที่ใช้ Linux) )

checkbashismsแน่นอนจะช่วยให้คุณหาbashismsแต่ไม่อาจชี้ให้เห็นไวยากรณ์ POSIX ที่ไม่เข้ากันกับหรือzshbash

ที่นี่หากคุณต้องการใช้bashรหัสเฉพาะ (เช่นการแก้ไขbashข้อบกพร่องนั้นโดยที่มันจะไม่อ่าน~/.bashrcในเชลล์ล็อกอินแบบโต้ตอบ) วิธีที่ดีกว่าคือการ~/.bash_profileทำเช่นนั้น (ก่อนหรือหลังการจัดหา~/.profileที่ที่คุณใส่เซสชันทั่วไป initialisations)


ที่ไม่ผ่านcheckbashismsเพราะตัวแปรที่ใช้
Thomas Dickey

2
@ThomasDickey ใช่ แต่เป็นกรณีที่รายงาน checkbashisms ควรถูกละเว้น โซลูชันอื่น ๆ ที่ให้มาทั้งหมดนั้นแย่กว่ามาก
Stéphane Chazelas

@ StéphaneChazelasคุณบอกว่าโซลูชันอื่น ๆ ทั้งหมดแย่ลง เกิดอะไรขึ้นกับการใช้$0สำหรับกรณีนี้?
Anthony G - ความยุติธรรมสำหรับ Monica

2
@AnthonyGeoghegan นั่นไม่ได้แยกแยะความแตกต่างระหว่างทุบตีเรียกว่าเป็นshและบางเปลือกอื่น ๆ shเรียกว่าเป็น
Gilles 'ดังนั้นหยุดความชั่วร้าย'

นอกจากนี้ยังให้ผลบวกที่ผิดพลาดหากdashมีการเรียกเชลล์ที่ไม่ใช่ bash อื่นซึ่งไม่ถูกต้องด้วยargv[0] == "bash"ซึ่งผิดกฎหมายทั้งหมด
Kevin

17

โดยปกติจะใช้$0เพื่อจุดประสงค์นี้ บนเว็บไซต์ที่คุณเชื่อมโยงมันย่อมาจาก:

0
   (Zero.) Expands to the name of the shell or shell script.

ง่ายมาก! ฉันคุ้นเคยกับการใช้$0ชื่อเชลล์สคริปต์จนฉันลืมว่ามันสามารถอ้างถึงเชลล์ปัจจุบันได้ ขอบคุณ!
Anthony G - ความยุติธรรมสำหรับ Monica

1
ตามปกติแล้วอนุสัญญานี้เป็นที่ยอมรับโดยโปรแกรมที่มีพฤติกรรมดีทั้งหมด แต่โปรแกรมที่เป็นอันตรายสามารถทำให้คุณยุ่งเหยิงได้โดยใช้การเรียกของระบบที่หลากหลายจากexecตระกูลเพราะพวกมันอนุญาตให้โปรแกรมที่เรียกใช้เพื่อระบุไบนารีที่จะทำงานแยกจากสตริง 0.
dmckee

นั่นใช้ได้กับ. profile :) cute
Rob

5

โดยปกติแล้วตัวแปรสภาพแวดล้อม SHELL จะบอกคุณถึงเปลือกเริ่มต้น คุณไม่จำเป็นต้องทำการจัดหาไฟล์. bashrc ด้วยตนเอง (ไม่เป็นความจริงดูการปรับปรุงซอลเบลโลว์) bash ควรทำสิ่งนี้โดยอัตโนมัติเพียงตรวจสอบให้แน่ใจว่ามันอยู่ในไดเรกทอรี $ HOME

ตัวเลือกอื่นคือทำสิ่งที่ชอบ:

 ps -o cmd= $$

ที่จะบอกคุณคำสั่ง (โดยไม่มีข้อโต้แย้ง, = ไม่มีค่าจะไม่แสดงส่วนหัวของคอลัมน์) ของกระบวนการปัจจุบัน ตัวอย่างผลลัพธ์:

 $ps -o cmd= $$
 bash
 $sh
 $ps -o cmd= $$
 sh

UPDATE:

ฉันยืนแก้ไข! :)

. bashrc ไม่ได้มีที่มาเสมอตามที่ได้กล่าวไว้ในความคิดเห็นและ /programming/415403/whats-the-difference-between-bashrc-bash-profile-and-environment

ดังนั้นคุณสามารถย้าย. bashrc ของคุณไปยัง. bash_profile และดูว่าใช้งานได้หรือไม่โดยไม่ต้องทำการทดสอบ ถ้าไม่ใช่คุณมีการทดสอบข้างต้น


1
.bashrcไม่ได้มาจากเปลือกของการเข้าสู่ระบบจริง ๆ แต่.profile(ถ้ามี) หรือ.bash_profileเป็น SHELLไม่ได้ระบุโดย POSIX และน่าเสียดายที่ไม่เป็นตัวเลือกรูปแบบcmd ps
Anthony G - ความยุติธรรมสำหรับ Monica

1
นี่คือสิ่งที่ฉันจะใช้ แต่ก็ขึ้นอยู่กับการจัดการที่เป็นอันตรายด้วย
dmckee

โปรดทราบว่าในขณะที่การps -o cmd= $$ทำงานควรจะสวยมากทุกที่$SHELLตัวแปรนั้นไม่เกี่ยวข้องอย่างสมบูรณ์ นั่นคือเชลล์เริ่มต้นของผู้ใช้และไม่มีผลกระทบต่อเชลล์ใดที่กำลังรันอยู่หรือเชลล์ใดกำลังรันเชลล์สคริปต์
terdon

2
ไม่จริงไม่ เชลล์จำนวนมากจะอ่าน~/.profileเมื่อเริ่มต้นเป็นเชลล์ล็อกอิน ดังนั้นสำหรับตัวอย่างเช่นคุณ$SHELLอาจจะเป็นและคุณเรียกcsh bash -lที่จะเริ่มต้นการทุบตีเป็นเปลือกเข้าสู่ระบบจึงจะอ่าน~/.profileแต่ของคุณจะยังคงชี้ไปที่$SHELL cshนอกจากนี้.profileสคริปต์อื่นอาจมีแหล่งที่มาอย่างชัดเจน เนื่องจาก OP กำลังดำเนินการเพื่อความทนทานสูงสุดควรพิจารณาทุกกรณี ไม่ว่าในกรณีใดก็ตาม$SHELLอย่าให้ข้อมูลที่เป็นประโยชน์เกี่ยวกับเชลล์ที่ทำงานอยู่ในปัจจุบัน
terdon

2
FWIW ฉันยืนแก้ไขเกินไป - SHELLไม่เกี่ยวกับการระบุโดย POSIX: pubs.opengroup.org/onlinepubs/9699919799/basedefs/ …
Anthony G - ความยุติธรรมสำหรับ Monica

4

คำถามจะถามเชลล์ล็อกอินของผู้ใช้รวมถึงเชลล์ปัจจุบันในลักษณะที่checkbashismsใช้ หากนั่นหมายความว่าเป็นเชลล์ที่ผู้ใช้ลงชื่อเข้าใช้ฉันจะใช้เชลล์จาก/etc/passwdเช่น

MY_UID=$(id -u)
MYSHELL=$(awk -F: '$3 == '$MY_UID'{ print $7; }' </etc/passwd )

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

บางคนอาจต้องการใช้getentมากกว่าแค่passwdไฟล์ (แต่นั่นไม่ได้อยู่ในขอบเขตของคำถาม)

ตามความคิดเห็นเกี่ยวกับ LDAP และข้อเสนอแนะสำหรับlognameรูปแบบทางเลือกนี้สามารถใช้:

MY_NAME=$(logname)
MYSHELL=$(getent passwd | awk -F: '$1 ~ /^'$MY_NAME'$/ {print $7;}' )

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


1
สมมติว่าฐานข้อมูลผู้ใช้อยู่ใน / etc / passwd (ไม่ใช่ LDAP / NIS / mysql ... ) และมีชื่อผู้ใช้เพียงชื่อเดียวต่อผู้ใช้ (น่าจะดีกว่าที่จะตรวจสอบคอลัมน์แรกกับ$(logname))
Stéphane Chazelas

iirc, POSIX ไม่ได้กำหนดยูทิลิตี้ที่จำเป็น (หรือฉันจะใช้มันในคำตอบของฉัน) ไม่ว่าจะใช้idหรือตัวแปรที่ผู้ใช้ปรับเปลี่ยนได้เป็นตัวเลือกที่แตกต่างกัน
Thomas Dickey

1
โปรดทราบว่าผมบอกว่าไม่$(logname) $LOGNAMEID ผู้ใช้และเปลือกเข้าสู่ระบบนั้นมาจากชื่อผู้ใช้ที่ใช้ในการเข้าสู่ระบบ คุณไม่สามารถรับกลับจาก ID ผู้ใช้ไปยังเชลล์การเข้าสู่ระบบได้ยกเว้นในระบบที่มีชื่อผู้ใช้เพียงชื่อเดียวต่อ ID ผู้ใช้ หรือ IOW คีย์หลักในฐานข้อมูลผู้ใช้คือชื่อผู้ใช้ไม่ใช่ uid
Stéphane Chazelas

ขอบคุณ @ThomasDickey สำหรับคำตอบที่มาจากมุมมองที่ต่างออกไป อย่างไรก็ตามมันซับซ้อนกว่าที่ฉันคิดเล็กน้อย (เหมือนคำตอบของjimmijและStéphane ) หนึ่งในเหตุผลที่ฉันวางไฟล์การกำหนดค่าเปลือกของฉันภายใต้การควบคุมเวอร์ชันคือการกำหนดค่าสภาพแวดล้อมของฉันในทุกระบบที่ฉันเข้าสู่ระบบเป็นประจำ: Cygwin, Ubuntu และ CentOS (ทั้ง 5 และ 7 โดยใช้ Active Directory สำหรับผู้ใช้ การตรวจสอบ) ด้วยเหตุนี้ฉันจึงต้องการให้ตรรกะนั้นง่ายที่สุดเท่าที่จะทำได้
Anthony G - ความยุติธรรมสำหรับ Monica
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.