ฉันจะตรวจสอบได้อย่างไรว่าฉันอยู่ในชั้นย่อย


24

ฉันพยายามเขียนฟังก์ชั่นเพื่อแทนที่ฟังก์ชั่นการทำงานของexitbuiltin เพื่อป้องกันตัวเองไม่ให้ออกจากเครื่อง

ฉันพยายามใช้SHLVLตัวแปรสภาพแวดล้อม แต่ดูเหมือนจะไม่เปลี่ยนแปลงภายใน subshells:

$ echo $SHLVL
1
$ ( echo $SHLVL )
1
$ bash -c 'echo $SHLVL'
2

ฟังก์ชั่นของฉันเป็นดังนี้:

exit () {
    if [[ $SHLVL -eq 1 ]]; then
        printf '%s\n' "Nice try!" >&2
    else
        command exit
    fi
}

สิ่งนี้จะไม่อนุญาตให้ฉันใช้exitภายใน subshells แม้ว่า:

$ exit
Nice try!
$ (exit)
Nice try!

เป็นวิธีที่ดีในการตรวจสอบว่าฉันอยู่ใน subshell หรือไม่



1
นั่นเป็นเพราะการนี้ $ SHLVL คือ 1 เพราะคุณยังอยู่ในระดับเชลล์ 1 แม้ว่าคำสั่ง echo $ SHLVL จะทำงานใน "subshell" จากการโพสต์นั้น subshells วางไข่ด้วยวงเล็บ(...)ได้รับคุณสมบัติทั้งหมดของกระบวนการพาเรนต์ คำตอบที่ให้ไว้คือโซลูชันที่มีประสิทธิภาพมากขึ้นในการกำหนดระดับเชลล์ของคุณ
kemotep

1
สำเนาที่เป็นไปได้ของฉันจะรับ pid ของ subshell ได้อย่างไร
mosvy

5
@mosvy ฉันรู้สึกว่าเป็นคำถามที่แตกต่าง เช่นBASH_SUBSHELLคำตอบ (แม้ว่าการโต้เถียง) จะไม่ใช้กับคำถามนั้น
Sparhawk

2
เห็นชื่อบน HNQ และคิดว่านี่เป็นคำถามกลศาสตร์ควอนตั ...
Mehrdad

คำตอบ:


43

ในทุบตีคุณสามารถเปรียบเทียบ$BASHPIDกับ$$

$ ( if [ "$$" -eq "$BASHPID" ]; then echo not subshell; else echo subshell; fi )
subshell
$   if [ "$$" -eq "$BASHPID" ]; then echo not subshell; else echo subshell; fi
not subshell

หากคุณไม่ได้อยู่ในสถานะทุบตี$$ควรจะยังคงเหมือนเดิมใน subshell ดังนั้นคุณต้องมีวิธีอื่นในการรับ ID กระบวนการจริงของคุณ

วิธีการหนึ่งที่จะได้รับ pid sh -c 'echo $PPID'ที่แท้จริงของคุณคือ หากคุณใส่มันลงไปในพื้นราบ( … )มันอาจจะดูไม่ทำงานเนื่องจากเชลล์ของคุณปรับส้อมให้เหมาะสม ลองใช้คำสั่ง no-op พิเศษ( : ; sh -c 'echo $PPID'; : )เพื่อทำให้มันคิดว่า subshell นั้นซับซ้อนเกินกว่าที่จะปรับให้เหมาะสม เครดิตไปที่John1024 บน Stack Overflowสำหรับวิธีการนั้น


คุณอาจต้องการที่จะเปลี่ยนที่ไป  (sh -c 'echo $PPID'; : )- ดูความคิดเห็นของฉันในคำตอบของ John1024
G-Man พูดว่า 'Reinstate Monica'

@ G-Man ดีนั่นเป็นเพียงการทดสอบ (เนื่องจากการใช้งานจริงมันจะเป็นสิ่งที่ซับซ้อนมากขึ้น) ... แต่ใช่จะดีที่สุดหากการทดสอบทำงานในเปลือกหอยทั้งหมด ดังนั้นฉันจึงไม่ใส่ op ก่อนและหลังหวังว่าจะจัดการทุกอย่าง
Derobert

38

แล้วไงBASH_SUBSHELLล่ะ

BASH_SUBSHELL
      เพิ่มขึ้นหนึ่งภายในแต่ละสภาพแวดล้อมของเชลล์หรือ subshell เมื่อเชลล์
      เริ่มดำเนินการในสภาพแวดล้อมนั้น ค่าเริ่มต้นคือ 0

$ echo $BASH_SUBSHELL
0
$ (echo $BASH_SUBSHELL)
1

16
มันจะเป็นคำสั่งที่สะดวกในภาพยนตร์ Inception
Eric Duminil

ในการลงทะเบียนอาจเป็น $ SHLVL
ยาย

19

[นี่ควรเป็นความคิดเห็น แต่ผู้ดูแลมักจะลบความคิดเห็นของฉันดังนั้นสิ่งนี้จะยังคงเป็นคำตอบที่ฉันสามารถใช้เป็นข้อมูลอ้างอิงแม้ว่าจะถูกลบ]

การใช้BASH_SUBSHELLไม่น่าเชื่อถืออย่างสมบูรณ์เนื่องจากตั้งค่าเป็น 1 ในsubshells บางตัวเท่านั้นไม่ใช่ใน subshells ทั้งหมด

$ (echo $BASH_SUBSHELL)
1
$ echo $BASH_SUBSHELL | cat
0

ก่อนที่จะอ้างว่ากระบวนการย่อยที่คำสั่งท่อจะดำเนินการในไม่ได้เป็นจริงๆ subshell จริงพิจารณานี้man bashข้อมูลโค้ด:

แต่ละคำสั่งในไปป์ไลน์จะถูกดำเนินการเป็นกระบวนการแยกต่างหาก (เช่นใน subshell)

และความหมายในทางปฏิบัติ - ไม่ว่าส่วนของสคริปต์จะเรียกใช้กระบวนการย่อยหรือไม่ซึ่งมีความสำคัญไม่ใช่คำศัพท์บางคำ

ทางออกเดียวที่ได้อธิบายไว้แล้วในคำตอบของคำถามนี้คือการตรวจสอบว่า$BASHPIDเท่ากับ$$หรือพกพา แต่มีประสิทธิภาพน้อยกว่ามาก:

if [ "$(exec sh -c 'echo "$PPID"')" != "$$" ]; then
    echo you\'re in a subshell
fi

11
Nit: BASH_SUBSHELLถูกตั้งค่าอย่างน่าเชื่อถือ แต่การได้รับค่าอย่างถูกต้องนั้นไม่แน่นอน สังเกตสิ่งที่เอกสารพูดว่า: "เพิ่มขึ้นทีละตัวในแต่ละสภาพแวดล้อมของ subshell หรือ subshell เมื่อเชลล์เริ่มดำเนินการในสภาพแวดล้อมนั้น " ฉันคิดว่าในตัวอย่างไปป์ bash ยังไม่ได้เริ่มดำเนินการใน subshell นั้นเมื่อตัวแปรถูกขยาย คุณสามารถเปรียบเทียบecho $BASH_VERSIONกับdeclare -p BASH_VERSION- หลังควรส่งออกที่ 1 อย่างน่าเชื่อถือกับท่องานพื้นหลัง ฯลฯ
muru

6
แม้พูดว่าeval 'echo $BASH_SUBSHELL $BASHPID' | catจะเอาท์พุท 1 สำหรับBASH_SUBSHELLเพราะตัวแปรจะขยายตัวหลังจากการดำเนินการได้เริ่ม
muru

4
อาร์กิวเมนต์เหล่านั้นทั้งหมดควรนำไปใช้กับการทดแทนกระบวนการและคำสั่งกระบวนการ bg แต่เป็นเพียงท่อที่แตกต่างกัน การดูรหัสการเพิ่มขึ้นsubshell_levelจริง ๆ ถูกเลื่อนออกไปในกรณีของท่อพื้นหน้าซึ่งอาจมีเหตุผลบางอย่าง แต่ที่ฉันไม่สามารถทำออกมาได้ ;-)
mosvy

2
คุณถูก. ดูเหมือนว่าเจตน์จะตั้งใจอย่างนั้น lists.gnu.org/archive/html/bug-bash/2015-06/msg00050.html : "subshells มาตรการ BASH_SUBSHELL (... ) ไม่ใช่องค์ประกอบไปป์ไลน์" lists.gnu.org/archive/html/bug-bash/2015-06/msg00054.html : "ฉันจะคิดว่าฉันควรจะบันทึกสถานะเดิมหรือขยายคำจำกัดความของ` subshell 'ที่ $ BASH_SUBSHELL สะท้อนหรือไม่ "
muru

2
@JoL คุณผิดการขยายตัวเกิดขึ้นในกระบวนการแยกต่างหากเช่นกันโปรดอ่านลิงก์และตัวอย่างจากการสนทนานี้ด้านบน echo $$ $BASHPID $BASH_SUBSHELL | catหรือเพียงแค่ลองกับ
mosvy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.