วิธีตรวจสอบว่าเชลล์คือล็อกอิน / โต้ตอบ / แบทช์


143

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

คำถามของฉันคือฉันจะทดสอบด้วยคำสั่ง / เงื่อนไขได้อย่างไรถ้าฉันใช้การโต้ตอบการล็อกอินหรือแบตช์เชลล์?

ฉันกำลังมองหาคำสั่งหรือเงื่อนไข (ที่ส่งคืนtrueหรือfalse) และฉันสามารถวางคำสั่ง if ตัวอย่างเช่น:

if [[ condition ]]
   echo "This is a login shell"
fi

3
ยังมีอีกคำถาม: STDIN และ / หรือ STDOUT เชื่อมต่อกับ tty (เทอร์มินัล) หรือไพพ์ (ไฟล์หรือกระบวนการ) หรือไม่? นี่คือการทดสอบที่เกี่ยวข้อง แต่แตกต่างกันตามที่อธิบายไว้ในความเห็นด้านล่าง
Mark Hudson

คำตอบ:


160

ฉันสมมติว่าbashเชลล์หรือคล้ายกันเนื่องจากไม่มีเชลล์แสดงอยู่ในแท็ก

วิธีตรวจสอบว่าคุณอยู่ในเชลล์แบบโต้ตอบหรือไม่:

[[ $- == *i* ]] && echo 'Interactive' || echo 'Not interactive'

วิธีตรวจสอบว่าคุณอยู่ในเชลล์ล็อกอินหรือไม่:

shopt -q login_shell && echo 'Login shell' || echo 'Not login shell'

โดย "แบทช์" ฉันถือว่าคุณหมายถึง "ไม่โต้ตอบ" ดังนั้นการตรวจสอบเชลล์เชิงโต้ตอบควรเพียงพอ


12
สำหรับzshผู้ใช้การตรวจสอบเชลล์ล็อกอินสามารถทำได้ด้วย:if [[ -o login ]] ...
chb

1
หากคุณต้องการทราบว่า "ผู้ใช้" เรียกใช้โปรแกรมของคุณกับ "cron" หรือไม่ [[TERM == "dumb"]] && echo "กำลังทำงานอยู่ใน cron
Erik Aronesty

10
@ErikAronesty ไม่ใช่ว่าเทอร์มินัลที่โง่เง่าทั้งหมดจะเป็นเซสชัน cron
Chris Down

2
“ ฉันสมมติว่าเชลล์ทุบตีหรือคล้ายกันเนื่องจากไม่มีเชลล์แสดงอยู่ในแท็ก” - ตรรกะของคำสั่งนี้ช่างสวยงามจริงๆ! :)
Michael Le Barbier Grünewald

2
@antonio นั่นเป็นเพราะข้อความของคุณไม่ถูกต้อง$-กำลังถูกขยายในเชลล์ปัจจุบัน หากคุณใช้เครื่องหมายคำพูดเดี่ยวรอบนิพจน์แบบเต็มคุณจะได้ผลลัพธ์ที่ถูกต้อง:bash -c '[[ $- == *i* ]] && echo "Interactive" || echo "Not interactive"'
Chris Down

33

ในเชลล์สไตล์ Bourne iตัวเลือกจะระบุว่าเชลล์เป็นแบบโต้ตอบหรือไม่:

case $- in
  *i*) echo "This shell is interactive";;
  *) echo "This is a script";;
esac

ไม่มีวิธีการทดสอบและพกพาที่เชื่อถือได้อย่างเต็มที่สำหรับเชลล์ล็อกอิน ksh และ zsh เพิ่มเพื่อl $-Bash ตั้งค่าlogin_shellตัวเลือกซึ่งคุณสามารถสอบถามshopt -q login_shellได้ ให้ทดสอบว่า$0เริ่มต้นด้วย-: เชลล์ปกติรู้หรือไม่ว่าเป็นเชลล์ล็อกอินเพราะผู้โทรเพิ่ม-คำนำหน้าเพื่อโต้แย้งศูนย์ (ปกติชื่อหรือพา ธ ของไฟล์เรียกทำงาน) สิ่งนี้ล้มเหลวในการตรวจหาวิธีเฉพาะเชลล์ในการเรียกใช้ล็อกอินเชลล์ (เช่นash -l)


(... ) เพราะผู้โทร - ฉันคิดว่ามีบางอย่างขาดหายไปในส่วนนี้
Piotr Dobrogost

มันจะเหมาะถ้า$0เริ่มต้นด้วยเสมอ-ไม่ว่าจะเริ่มอย่างไร แต่มีข้อยกเว้นอย่างน้อยหนึ่งข้อ: สิ่งนี้ไม่จริงเสมอกับการทุบตีแม้ว่ามันควรจะเป็นเปลือกเข้าสู่ระบบ ลองในกล่องของคุณ: เรียกbash --loginแล้วยังคงอ่าน$0 bash
Wirawan Purwanto

@WirawanPurwanto ฉันไม่แน่ใจว่าฉันเข้าใจความคิดเห็นของคุณ นั่นคือสิ่งที่ฉันเขียนในประโยคสุดท้ายของฉันหรือไม่
Gilles

@Gilles: ถูกต้อง ขอโทษฉันพลาดประโยคสุดท้ายของคุณ
Wirawan Purwanto

24

ตู้ปลา

นี่คือคำตอบสำหรับfishในกรณีที่ผู้ใช้รายอื่นสะดุดกับหน้านี้

if status --is-interactive
    # ...
end

if status --is-login
    # ...
end

echo "darn, I really wanted to have to use globs or at least a case statement"

เอกสารปลา: การเริ่มต้น


1
-1 สำหรับการแก้ไขที่ไม่จำเป็น
Nick Bastin

6
หากใครสงสัยในความคิดเห็นของ @ NickBastin เขามีจุดพอใช้ดังนั้นฉันจึงทำการแก้ไข ตอนนี้จำนวนของความบ้าคลั่งลดลงครึ่งหนึ่งแล้ว
ohspite

18

csh / tcsh

สำหรับcshและtcshฉันมีดังต่อไปนี้ใน.cshrcไฟล์ของฉัน:

if($?prompt) then               # Only interactive shells set $prompt
    ...
endif

โดยเฉพาะสำหรับtcshตัวแปรloginshที่กำหนดไว้สำหรับเปลือกเข้าสู่ระบบ:

if($?loginsh) then              # A login shell..
    ...
endif

( tcshนอกจากนี้ยังมีตัวแปรshlvlที่ตั้งค่าเป็นจำนวนของเชลล์ที่ซ้อนกันโดยที่ล็อกอินเชลล์มีค่า 1)


2
PS1ไม่ทำงานเพื่อทดสอบเปลือกโต้ตอบ มันเกือบจะตั้งค่าในการโต้ตอบ แต่คุณสามารถยกเลิกได้ มันมากมักจะตั้งอยู่ในเปลือกไม่โต้ตอบเพราะหลายระบบเรือที่มีในexport PS /etc/profile
Gilles

@Gilles ขอบคุณสำหรับการแก้ไขและแก้ไข
Andrew Stein

17

อีกวิธีคือตรวจสอบผลลัพธ์ของ tty

if [ "`tty`" != "not a tty" ]; then

10
... หรือใช้[ -t 0 ]เพื่อทดสอบว่า STDIN เป็น tty หรือไม่ คุณยังสามารถใช้ 1 (STDOUT) หรือ 2 (STDERR) ตามความต้องการของคุณ
Derobert

@derobert - ขอบคุณที่แสดงให้ฉันเห็นสิ่งใหม่ ๆ
Adrian Cornish

10
นี่คือการทดสอบที่แตกต่างกัน เป็นไปได้ที่จะมีเชลล์แบบไม่โต้ตอบซึ่งอินพุตเป็นเทอร์มินัล (ทุกครั้งที่คุณเรียกใช้สคริปต์ในเทอร์มินัล!) และเป็นไปได้ (แม้ว่าจะหายาก) เพื่อให้เชลล์แบบโต้ตอบรับอินพุตจากเทอร์มินัล
Gilles

@Gilles หากเชลล์เป็นแบบโต้ตอบและปิดการปล่อยให้เด็กdisownและมีชีวิตอยู่ttyทำงานได้ดีที่สุดที่จะรู้ว่ามันไม่ได้เป็นแบบโต้ตอบอีกต่อไปในขณะที่$-ไม่เปลี่ยน ฉันยังงงกับสิ่งที่เป็นวิธีที่ดีที่สุด
กุมภ์กุมภ์

5
@AquariusPower แล้วสิ่งที่คุณต้องการทดสอบนั้นไม่ได้มีไว้สำหรับเชลล์แบบโต้ตอบ แต่จะว่าอินพุตมาตรฐานเป็นเทอร์มินัลหรือไม่ [ -t 0 ]ใช้ ป.ล. ในความคิดเห็นก่อนหน้าของฉันฉันเขียนว่า "มีความสัมพันธ์ที่แข็งแกร่ง" - ฉันลืม "นอกเหนือจากกรณีทั่วไปของสคริปต์ที่เริ่มต้นด้วย#!/bin/shหรือคล้ายกัน" ซึ่งเป็นแบบไม่โต้ตอบ แต่สามารถเชื่อมต่อกับสถานี
Gilles

8

UNIX / Linux มีคำสั่งให้ตรวจสอบว่าคุณอยู่บนเทอร์มินัลหรือไม่

if tty -s
then
echo Terminal
else
echo Not on a terminal
fi

1
งานdashนี้ยัง
Ken Sharp


3

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

if [ -z $PS1 ] # no prompt?
### if [ -v PS1 ]   # On Bash 4.2+ ...
then
  # non-interactive
  ...
else
  # interactive
  ...
fi

สิ่งนี้ฉันได้เรียนรู้ที่นี่: https://www.tldp.org/LDP/abs/html/intandnonint.html


1

iไม่ใช่ตัวเลือกที่ถูกต้องในการค้นหา -iคือการบังคับให้เชลล์ที่ไม่ใช่แบบโต้ตอบโต้ตอบกัน ตัวเลือกการเปิดใช้งานอัตโนมัติที่ถูกต้องคือ-sแต่ Bash น่าเสียดายที่ไม่สามารถจัดการได้อย่างถูกต้อง

คุณต้องตรวจสอบว่า$-มีs(สิ่งนี้ได้รับอนุญาตให้เปิดใช้งานอัตโนมัติ) หรือไม่ว่ามีi(สิ่งนี้ไม่ได้รับอนุญาตให้เปิดใช้งานอัตโนมัติ แต่อย่างเป็นทางการเท่านั้นคู่กับ-iตัวเลือกบรรทัดคำสั่งของเปลือก)


1
sจะเป็นถ้าเชลล์อ่านคำสั่งจาก stdin ไม่ว่าจะเป็นแบบโต้ตอบ เชลล์แบบโต้ตอบไม่จำเป็นต้องอ่านคำสั่งจาก stdin (ลองzsh -i < /dev/nullแม้ว่า zsh ดูเหมือนจะเป็นข้อยกเว้นที่นี่) และเชลล์อาจอ่านคำสั่งจาก stdin และไม่ใช่แบบโต้ตอบ (เช่นsh < fileหรือecho 'echo "$1"' | sh -s foo bar)
Stéphane Chazelas

2
สิ่งที่ฉันต้องการชี้ให้เห็นก็คือ Bourne Shell ดั้งเดิมไม่มี 'i' ในราคา $ - แม้ว่ามันจะตั้งใจให้มีการโต้ตอบ
schily

ตกลง แต่บน U&L "shell" มักอ้างถึงshells สมัยใหม่ โดยทั่วไปแล้ว Bourne shell นั้นถือว่าเป็นreliqueและตัวแปรของคุณเองนั้นไม่สำคัญพอที่จะคาดหวังให้คนอื่นรู้ว่าคุณกำลังพูดถึงเรื่องอะไรถ้าคุณไม่อธิบายให้ชัดเจน คำถามที่กล่าวถึง[[...]]ซึ่งหมายถึง ksh / bash / zsh คุณมีจุดบันทึกประวัติศาสตร์ที่การตรวจสอบiใน$-จะไม่ทำงานในเชลล์เป้าหมาย แต่การตรวจสอบแล้วsจะไม่ทำงานที่นั่นอย่างน่าเชื่อถือ คุณต้องการตรวจสอบ[ -t 0 ]หรือi; แม้ว่าจะถูกหลอกในมุมกรณีเช่นecho 'exec < /dev/tty; that-check' | sh'
Stéphane Chazelas

Solaris ถึง Solaris 10 มาพร้อมกับ Bourne Shell ดั้งเดิมและแม้แต่ sill รวมถึง aprox มีข้อบกพร่อง 10 ประการที่ทราบว่าอยู่ในเชลล์ตั้งแต่ SVr4 ดังนั้นการเพิ่มคำใบ้บน Bourne Shell ไม่ใช่การใส่หมวกอย่างที่คุณอาจเชื่อ zsh ในอีกด้านหนึ่งไม่รองรับเช่นมันล้มเหลวเมื่อคุณพยายามเรียกใช้ "กำหนดค่า" ด้วย zsh ดังนั้นควรตั้งค่า / bin / sh ให้ชี้ไปที่ zsh BTW: Bourne Shell ของฉันตั้งค่า -i ตามค่าเริ่มต้นในกรณีที่ตัดสินใจเป็นแบบโต้ตอบ
Schily

3
ฉันสังเกตเห็นว่า POSIX ดูเหมือนจะไม่ต้องการ $ - ประกอบด้วย i (ซึ่งดูเหมือนว่าต้องมี s) ฉันจะตั้งคำถามกับ ML กลุ่ม austin
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.