ตัวแปร $ BASH_COMMAND ดีอย่างไร


25

ตามคู่มือ Bashตัวแปรสภาพแวดล้อมBASH_COMMANDมี

คำสั่งที่กำลังดำเนินการหรือกำลังจะถูกดำเนินการยกเว้นว่าเชลล์กำลังดำเนินการคำสั่งเป็นผลมาจากกับดักซึ่งในกรณีนี้มันเป็นคำสั่งที่ดำเนินการในเวลาที่กับดัก

การเอามุมของมุมกับดักนั้นไว้ถ้าฉันเข้าใจถูกต้องนี่หมายความว่าเมื่อฉันรันคำสั่งตัวแปรจะBASH_COMMANDมีคำสั่งนั้นอยู่ ไม่ชัดเจนว่าตัวแปรนั้นไม่มีการตั้งค่าหลังจากการดำเนินการคำสั่ง (กล่าวคือเป็นเพียง avaialble ขณะที่คำสั่งกำลังทำงาน แต่ไม่ใช่หลังจาก) แม้ว่าจะมีใครโต้แย้งว่าเป็นเพราะ "คำสั่งที่กำลังดำเนินการอยู่หรือกำลังจะถูกดำเนินการ" มันไม่ใช่คำสั่งที่เพิ่งถูกเรียกใช้งาน

แต่ให้ตรวจสอบ:

$ set | grep BASH_COMMAND=
$ 

ว่างเปล่า ฉันคาดว่าจะเห็นBASH_COMMAND='set | grep BASH_COMMAND='หรืออาจจะเป็นเพียงBASH_COMMAND='set'แต่ว่างเปล่าทำให้ฉันประหลาดใจ

ลองทำอย่างอื่น:

$ echo $BASH_COMMAND
echo $BASH_COMMAND
$ 

ดีที่ทำให้รู้สึก ผมรันคำสั่งecho $BASH_COMMANDและตัวแปรมีสตริงBASH_COMMAND echo $BASH_COMMANDทำไมเวลานี้ถึงใช้งานได้ แต่ไม่ใช่ก่อนหน้านี้

ลองทำsetอีกครั้ง:

$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$

ดังนั้นรอ มันถูกตั้งค่าเมื่อฉันเรียกใช้งานechoคำสั่งนั้นและจะไม่ถูกยกเลิกหลังจากนั้น แต่เมื่อฉันดำเนินการsetอีกครั้งBASH_COMMAND ไม่ได้ตั้งค่าsetคำสั่ง ไม่ว่าฉันจะรันsetคำสั่งที่นี่บ่อยแค่ไหนผลลัพธ์ก็ยังคงเหมือนเดิม ดังนั้นตัวแปรถูกตั้งค่าเมื่อดำเนินการechoแต่ไม่เมื่อดำเนินการset? มาดูกัน.

$ echo Hello AskUbuntu
Hello AskUbuntu
$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$

อะไร? ดังนั้นตัวแปรถูกตั้งเมื่อผมดำเนินการecho $BASH_COMMANDแต่ไม่ได้เมื่อผมดำเนินการecho Hello AskUbuntu? ตอนนี้ความแตกต่างอยู่ที่ไหน ตัวแปรตั้งค่าเฉพาะเมื่อคำสั่งปัจจุบันบังคับให้เชลล์ประเมินตัวแปรหรือไม่? ลองทำสิ่งที่แตกต่าง อาจมีคำสั่งจากภายนอกในเวลานี้ไม่ใช่ bash builtin สำหรับการเปลี่ยนแปลง

$ /bin/echo $BASH_COMMAND
/bin/echo $BASH_COMMAND
$ set | grep BASH_COMMAND=
BASH_COMMAND='/bin/echo $BASH_COMMAND'
$

อืมโอเค ... อีกครั้งตัวแปรถูกตั้งค่า ดังนั้นการเดาปัจจุบันของฉันถูกต้องหรือไม่ มีการตั้งค่าตัวแปรเฉพาะเมื่อต้องมีการประเมินหรือไม่? ทำไม? ทำไม? ด้วยเหตุผลด้านประสิทธิภาพ? ลองทำอีกครั้ง เราจะพยายาม grep สำหรับ$BASH_COMMANDในไฟล์และ$BASH_COMMANDจากนั้นควรมีgrepคำสั่งgrepควร grep สำหรับgrepคำสั่งนั้น(เช่นสำหรับตัวเอง) ดังนั้นให้สร้างไฟล์ที่เหมาะสม:

$ echo -e "1 foo\n2 grep\n3 bar\n4 grep \$BASH_COMMAND tmp" > tmp
$ grep $BASH_COMMAND tmp
grep: $BASH_COMMAND: No such file or directory
tmp:2 grep                                      <-- here, the word "grep" is RED
tmp:4 grep $BASH_COMMAND tmp                    <-- here, the word "grep" is RED
tmp:2 grep                                      <-- here, the word "grep" is RED
tmp:4 grep $BASH_COMMAND tmp                    <-- here, the word "grep" is RED
$ set | grep BASH_COMMAND=
BASH_COMMAND='grep --color=auto $BASH_COMMAND tmp'
$

ตกลงน่าสนใจ คำสั่งgrep $BASH_COMMAND tmpได้ขยายไปgrep grep $BASH_COMMAND tmp tmp(ตัวแปรที่ได้รับการขยายตัวเพียงว่าครั้งหนึ่งของหลักสูตร) และดังนั้นผมจึง grepped สำหรับgrepครั้งหนึ่งในไฟล์ที่ไม่อยู่และเป็นครั้งที่สองในแฟ้ม$BASH_COMMANDtmp

Q1:สมมติฐานปัจจุบันของฉันถูกต้องที่:

  • BASH_COMMANDถูกตั้งค่าเมื่อคำสั่งพยายามประเมินค่าจริงเท่านั้น และ
  • มันไม่ได้ล้างคำสั่งหลังจากการดำเนินการของคำสั่งแม้ว่าคำอธิบายอาจทำให้เราเชื่อเช่นนั้น?

Q2:ถ้าใช่ทำไม ประสิทธิภาพ? หากไม่สามารถอธิบายพฤติกรรมในลำดับคำสั่งด้านบนได้อย่างไร

คำถามที่ 3:ท้ายที่สุดมีสถานการณ์ใดบ้างที่ตัวแปรนี้สามารถใช้อย่างมีความหมายได้จริงหรือไม่? จริง ๆ แล้วฉันพยายามใช้ภายใน$PROMPT_COMMANDเพื่อวิเคราะห์คำสั่งที่ถูกดำเนินการ (และทำบางสิ่งขึ้นอยู่กับว่า) แต่ฉันทำไม่ได้เพราะทันทีที่ภายในฉัน$PROMPT_COMMANDฉันเรียกใช้คำสั่งเพื่อดูตัวแปร$BASH_COMMANDตัวแปร รับชุดเป็นคำสั่งที่ แม้เมื่อฉันทำMYVARIABLE=$BASH_COMMANDถูกต้องที่จุดเริ่มต้นของฉัน$PROMPT_COMMANDแล้วMYVARIABLEมีสตริงMYVARIABLE=$BASH_COMMANDเพราะการกำหนดเป็นคำสั่งด้วย (คำถามนี้ไม่ได้เกี่ยวกับวิธีที่ฉันจะได้รับคำสั่งปัจจุบันภายในการ$PROMPT_COMMANDดำเนินการมีหลายวิธีที่ฉันรู้)

มันค่อนข้างคล้ายกับหลักการความไม่แน่นอนของไฮเซนเบิร์ก เพียงแค่สังเกตตัวแปรฉันก็เปลี่ยนมัน


5
ดี แต่อาจเหมาะสมกว่าสำหรับunix.stackexchange.com ... มีbashüber-gurus อยู่ที่นั่นจริง
Rmano

เป็นไปได้ไหมที่จะย้ายมันไปที่นั่น? Mods?
Malte Skoruppa

ฉันไม่แน่ใจว่าจะทำอย่างไร --- ฉันคิดว่ามันเป็นสิทธิ์ของ Mod ในทางกลับกันฉันไม่คิดว่าการตั้งค่าสถานะเป็นเรื่องที่สมเหตุสมผล --- ลองดูว่ามีใครทำหน้าที่ปิดการตั้งค่าสถานะหรือไม่
Rmano

1
สำหรับจุดที่สามนี่คือสิ่งที่คุณกำลังค้นหา อาจขึ้นอยู่กับบทความที่คุณสามารถหาคำตอบสำหรับสองจุดแรกเช่นกัน
Radu Rădeanu

2
+1 ทำให้ฉันสับสน แต่ฉันสนุกที่จะอ่าน!
โจ

คำตอบ:


15

ตอบคำถามที่สาม: แน่นอนว่ามันสามารถใช้งานได้อย่างมีความหมายในแบบที่ Bash manual บอกใบ้อย่างชัดเจน - ในกับดักเช่น:

$ trap 'echo ‘$BASH_COMMAND’ failed with error code $?' ERR
$ fgfdjsa
fgfdjsa: command not found
fgfdjsa failed with error code 127
$ cat /etc/fgfdjsa
cat: /etc/fgfdjsa: No such file or directory
cat /etc/fgfdjsa failed with error code 1

อ่าดีจริงๆ ดังนั้นคุณจะบอกว่ากับดักเป็นบริบทเดียวที่ตัวแปรนี้มีเหตุผล? มันฟังดูเหมือนตัวพิมพ์มุมในคู่มือ: "คำสั่งที่ถูกดำเนินการ (... ) ยกเว้นว่าเชลล์กำลังประมวลผลคำสั่งอันเป็นผลมาจากกับดัก ... "
Malte Skoruppa

หลังจากอ่านบทความที่เชื่อมโยงในคำตอบโดย @DocSalvager เป็นที่ชัดเจนว่า$BASH_COMMANDสามารถนำไปใช้อย่างมีความหมายแม้ในบริบทอื่นนอกเหนือจากกับดัก แต่การใช้งานที่คุณอธิบายนั้นน่าจะเป็นเรื่องปกติและตรงไปตรงมาที่สุด ดังนั้นคำตอบของคุณและหนึ่งโดย DocSalvager ตอบคำถามQ3ของฉันได้ดีที่สุดและนี่คือสิ่งสำคัญที่สุดสำหรับฉัน สำหรับ Q1 และ Q2 ตามที่ระบุโดย @zwets สิ่งเหล่านั้นจะไม่ได้กำหนด อาจ$BASH_COMMANDเป็นเพราะได้รับค่าเมื่อเข้าถึงเพื่อประสิทธิภาพเท่านั้นมิฉะนั้นเงื่อนไขบางอย่างของโปรแกรมภายในแปลก ๆ อาจนำไปสู่พฤติกรรมดังกล่าว ใครจะรู้?
Malte Skoruppa

1
บน sidenote ฉันค้นพบว่าคุณสามารถบังคับ$BASH_COMMANDให้ตั้งค่าได้เสมอโดยบังคับให้ประเมิน$BASH_COMMANDก่อนคำสั่งแต่ละคำสั่ง ในการทำเช่นนี้เราสามารถใช้กับดัก DEBUG ซึ่งดำเนินการก่อนทุกคำสั่งเดียว (ทั้งหมดของ em) ถ้าเราออกเช่นtrap 'echo "$BASH_COMMAND" > /dev/null' DEBUGนั้น$BASH_COMMANDมักจะได้รับการประเมินก่อนที่แต่ละคำสั่ง (และได้รับมอบหมายคุณค่าของว่า$COMMAND) ดังนั้นถ้าฉันดำเนินการset | grep BASH_COMMAND=ขณะที่กับดักนั้นทำงานอยู่ ... คุณรู้อะไรบ้าง ตอนนี้การส่งออกเป็นBASH_COMMAND=setไปตามคาด :)
Malte Skoruppa

6

ตอนนี้มีการตอบคำถามในไตรมาสที่ 3 (ในความเห็นของฉัน: BASH_COMMANDมีประโยชน์ในกับดักและแทบจะทุกที่) ลองมาถ่าย Q1 และ Q2 กัน

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

คำตอบของ Q2 "ถ้าไม่ใช่จะอธิบายพฤติกรรมในลำดับคำสั่งด้านบนได้อย่างไร" ตามด้วยเหตุผล (ถ้าค่อนข้างอวดรู้): มันอธิบายได้ด้วยข้อเท็จจริงที่ว่าค่าของBASH_COMMANDไม่ได้กำหนด เนื่องจากค่าไม่ได้ถูกกำหนดจึงสามารถมีค่าใด ๆ ซึ่งเป็นสิ่งที่ลำดับแสดง

ป.ล.

มีจุดหนึ่งที่ฉันคิดว่าคุณกำลังตีจุดอ่อนในสเป็คจริง ๆ เป็นที่ที่คุณพูดว่า:

แม้เมื่อฉันทำ MYVARIABLE = $ BASH_COMMAND ที่จุดเริ่มต้นของ $ PROMPT_COMMAND ของฉัน MYVARIABLE จะมีสตริง MYVARIABLE = $ BASH_COMMAND เพราะการกำหนดเป็นคำสั่งด้วยเช่นกัน

วิธีที่ฉันอ่านหน้าคนทุบตีบิตในตัวเอียงไม่เป็นความจริง ส่วนนี้SIMPLE COMMAND EXPANSIONอธิบายถึงการกำหนดตัวแปรต่าง ๆ ในบรรทัดคำสั่งไว้ก่อนแล้ว

หากไม่มีคำสั่งชื่อผลลัพธ์ [ในคำอื่น ๆ มีเพียงการมอบหมายตัวแปร] การกำหนดตัวแปรที่มีผลต่อสภาพแวดล้อมของเชลล์ในปัจจุบัน

สิ่งนี้ชี้ให้เห็นว่าการกำหนดตัวแปรไม่ใช่คำสั่ง (และยกเว้นจากการแสดงในBASH_COMMAND) เช่นในภาษาการเขียนโปรแกรมอื่น นี้ก็จะยังอธิบายว่าทำไมไม่มีบรรทัดBASH_COMMAND=setในการส่งออกของset, setหลักเป็นประโยค 'เครื่องหมาย' สำหรับการกำหนดตัวแปร

OTOH ในย่อหน้าสุดท้ายของส่วนนั้นพูดว่า

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

... ซึ่งแนะนำเป็นอย่างอื่นและการกำหนดตัวแปรก็เป็นคำสั่งด้วยเช่นกัน


1
เพียงเพราะข้อสันนิษฐานบางอย่างที่ไม่สามารถกำหนดได้ไม่ได้หมายความว่าไม่ถูกต้อง Einstein สันนิษฐานว่าความเร็วของแสงนั้นเท่ากันสำหรับผู้สังเกตการณ์ใด ๆ (ที่ความเร็วใด ๆ ) เป็นหนึ่งในสมมติฐานพื้นฐานสำหรับทฤษฎีสัมพัทธภาพ; ที่ไม่สามารถสร้างได้ แต่นี่ไม่ได้หมายความว่ามันไม่ถูกต้อง คุณสมบัติ "สามารถสร้างได้" และ "ถูกต้อง" เป็นแบบมุมฉาก อย่างดีที่สุดคุณสามารถพูดว่า "เราไม่รู้ว่าถูกต้องหรือไม่เพราะไม่สามารถสร้างได้" นอกจากนี้ยังมีการระบุ: มันเป็นคำสั่งปัจจุบันในระหว่างการดำเนินการ ดังนั้นถ้าฉันพิมพ์setฉันควรเห็นBASH_COMMAND='set'บางแห่งในผลลัพธ์นั้นซึ่ง
Malte Skoruppa

... อย่างไรก็ตามไม่ใช่กรณี แม้ว่าที่อยู่ระหว่างการดำเนินการsetคำสั่ง ดังนั้นตามรายละเอียดมันควรจะทำงาน ดังนั้นการใช้งานที่ไม่ได้ปฏิบัติตามข้อกำหนดอย่างซื่อสัตย์หรือฉันเข้าใจผิดรายละเอียดหรือไม่ การค้นหาคำตอบสำหรับคำถามนั้นเป็นจุดประสงค์ของ Q1 +1 สำหรับ postscript ของคุณ :)
Malte Skoruppa

@MalteSkoruppa คะแนนดี setคงคำตอบตามและเพิ่มคำพูดเกี่ยวกับ
zwets

1
เป็นไปได้ว่าในตัวแปล bash setไม่ใช่ "คำสั่ง" เช่นเดียวกับที่=ไม่ใช่ "คำสั่ง" การประมวลผลข้อความต่อไปนี้ / รอบโทเค็นเหล่านั้นอาจมีตรรกะที่แตกต่างอย่างสิ้นเชิงกว่าการประมวลผลของคำสั่ง "
DocSalvager

คะแนนที่ดีจากคุณทั้งคู่ :) อาจเป็นได้ว่าsetไม่นับเป็น "คำสั่ง" ฉันไม่คิดว่า$BASH_COMMANDจะกลายเป็นว่าไม่ได้ให้ความสำคัญในหลาย ๆ สถานการณ์ ฉันเดาว่าอย่างน้อยที่สุดเราได้กำหนดไว้แล้วว่าหน้าคนจะมีความชัดเจนมากขึ้นในเรื่องนี้)
Malte Skoruppa

2

การใช้งานที่สร้างสรรค์สำหรับ $ BASH_COMMAND

เมื่อเร็ว ๆ นี้พบว่าการใช้ $ BASH_COMMAND ที่น่าประทับใจในการใช้งานฟังก์ชั่นเหมือนมาโคร

นี่คือเคล็ดลับหลักของนามแฝงและแทนที่การใช้กับดัก DEBUG หากคุณอ่านส่วนในโพสต์ก่อนหน้าเกี่ยวกับกับดัก DEBUG คุณจะจำตัวแปร $ BASH_COMMAND ในโพสต์นั้นฉันบอกว่ามันถูกตั้งค่าเป็นข้อความของคำสั่งก่อนที่จะเรียกแต่ละครั้งเพื่อดักจับ DEBUG มันกลับกลายเป็นว่ามันถูกตั้งค่าก่อนการดำเนินการของทุกคำสั่ง, กับดัก DEBUG หรือไม่ (เช่นรัน 'echo“ คำสั่งนี้ = $ BASH_COMMAND” เพื่อดูสิ่งที่ฉันกำลังพูดถึง) โดยการกำหนดตัวแปร (สำหรับบรรทัดนั้น) เราจับ BASH_COMMAND ที่ขอบเขตด้านนอกสุดของคำสั่งซึ่งจะมีคำสั่งทั้งหมด

ผู้เขียนบทความก่อนหน้านี้trapนอกจากนี้ยังมีพื้นหลังที่ดีบางอย่างในขณะที่การนำเทคนิคที่ใช้แก้ปัญหา The trapถูกกำจัดในเวอร์ชั่นที่ปรับปรุงแล้ว


+1 ในที่สุดฉันก็ไปอ่านบทความทั้งสองนี้ ว้าว! อ่านอะไรดี! ตรัสรู้มาก ผู้ชายคนนั้นเป็นกูรูทุบตี! รุ่งโรจน์! นี้ยังตอบความคิดเห็นของฉันกับคำตอบของ Dmitry กับว่าสามารถนำมาใช้มีความหมายในบริบทอื่นที่ไม่ใช่$BASH_COMMAND trapและสิ่งที่ใช้นั่นคือ! ใช้มันเพื่อใช้คำสั่งแมโครใน bash ใครจะคิดว่ามันเป็นไปได้? ขอบคุณสำหรับตัวชี้
Malte Skoruppa

0

การใช้งานทั่วไปที่ดูเหมือนว่ากับดัก ... การดีบั๊กคือการปรับปรุงชื่อที่ปรากฏในรายการหน้าต่าง (^ A ") เมื่อคุณใช้" หน้าจอ "

ฉันพยายามทำให้ "หน้าจอ", "รายการหน้าต่าง" ใช้งานได้มากกว่าดังนั้นฉันจึงเริ่มค้นหาบทความที่อ้างอิง "กับดัก ... ดีบั๊ก"

ฉันพบว่าวิธีการที่ใช้ PROMPT_COMMAND เพื่อส่ง "ลำดับชื่อโมฆะ" ไม่ได้ผลดีดังนั้นฉันจึงย้อนกลับไปที่กับดัก ... วิธีการแก้ปัญหา

นี่คือวิธีที่คุณทำ:

1 บอก "หน้าจอ" เพื่อค้นหาลำดับการหลบหนี (เปิดใช้งาน) โดยใส่ "shelltitle '$ | bash:'" ลงใน "$ HOME / .screenrc"

2 ปิดการแพร่กระจายของกับดักตรวจแก้จุดบกพร่องเป็น sub-shells นี่เป็นสิ่งสำคัญเพราะถ้าเปิดใช้งานทุกอย่างจะถูกบดบังให้ใช้: "set + o functrace"

3 ส่งลำดับการยกเว้นหัวเรื่องสำหรับ "หน้าจอ" เพื่อแปลความหมาย: กับดัก 'printf "\ ek $ (วันที่ +% Y% m% d% H% M% S) $ (whoami) @ $ (ชื่อโฮสต์): $ (pwd) $ {BASH_COMMAND} \ e \ "'" DEBUG "

มันไม่สมบูรณ์แบบ แต่มันช่วยและคุณสามารถใช้วิธีนี้เพื่อใส่ทุกอย่างที่คุณต้องการลงในชื่อ

ดู "windowlist" ต่อไปนี้ (5 หน้าจอ):

ค่าสถานะชื่อ NUM

1 bash: 20161115232035 mcb @ ken007: / home / mcb / ken007 RSYNCCMD = "sudo rsync" myrsync.sh -R -r "$ {1}" "$ {BACKUPDIR} $ {2: +" / $ {2} " } "$ 2 bash: 20161115230434 mcb @ ken007: / home / mcb / ken007 ls --color = auto -la $ 3 bash: 20161115230504 mcb @ ken007: / home / mcb / ken007 cat bin / psg.sh $ 4 bash: 20161115222415 mcb @ ken007: / home / mcb / ken007 ssh ken009 $ 5 ทุบตี: 20161115222450 mcb @ ken007: / home / mcb / ken007 mycommoncleanup $

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