หยุดการติดตามการดำเนินการ bash (set -x) จากด้านนอกของสคริปต์


17

ฉันพยายามหาคำตอบสำหรับคำถามนี้ แต่ก็ยังไม่มีโชค:

ฉันมีสคริปต์ที่รันสคริปต์อื่น ๆ และสคริปต์อื่น ๆ เหล่านั้นมี "set -x" ในตัวพวกเขาซึ่งทำให้พวกเขาพิมพ์ทุกคำสั่งที่เรียกใช้ ฉันต้องการกำจัดสิ่งนั้น แต่เก็บข้อมูลไว้หากสคริปต์ใดส่งข้อความข้อผิดพลาดไปยัง stderr

ดังนั้นฉันจึงไม่สามารถเขียน ./script 2>/dev/null

นอกจากนี้ฉันไม่มีสิทธิ์ในการแก้ไขสคริปต์อื่น ๆ เหล่านั้นดังนั้นฉันจึงไม่สามารถเปลี่ยนตัวเลือกชุดได้ด้วยตนเอง

ฉันกำลังคิดเกี่ยวกับการบันทึกทุกอย่างตั้งแต่ stderr ไปยังไฟล์แยกต่างหากและกรองคำสั่งการติดตาม แต่อาจจะมีวิธีที่ง่ายกว่านี้?


1
./script 2>some_file
Satō Katsura

คำตอบ:


25

ด้วยbash4.1 ขึ้นไปคุณสามารถทำได้

BASH_XTRACEFD=7 ./script.bash 7> /dev/null

(ใช้ได้เมื่อbashเรียกใช้เช่นกันsh)

โดยทั่วไปเราบอกกำลังbashจะเอาท์พุทxtraceเอาท์พุทไฟล์อธิบาย 7 แทนการเริ่มต้นของ 2, /dev/nullและการเปลี่ยนเส้นทางที่จะอธิบายไฟล์ หมายเลข fd โดยพลการ ใช้ fd ด้านบน 2 ที่ไม่ได้ใช้ในสคริปต์ของคุณ หากเชลล์ที่คุณป้อนคำสั่งนี้เป็นbashหรือyashคุณสามารถใช้ตัวเลขที่สูงกว่า 9 (แม้ว่าคุณอาจพบปัญหาหากตัวบ่งชี้ไฟล์ถูกใช้ภายในโดยเชลล์)

หากเชลล์ที่คุณเรียกใช้bashสคริปต์นั้นคือzshคุณสามารถทำ:

(export BASH_XTRACEFD; ./script.bash {BASH_XTRACEFD}> /dev/null)

สำหรับตัวแปรที่จะถูกกำหนดโดยอัตโนมัติ fd ฟรีครั้งแรกที่สูงกว่า 9

สำหรับเวอร์ชันเก่าของbashตัวเลือกอื่นหากxtraceเปิดใช้งานด้วยset -x(ตรงข้ามกับ#! /bin/bash -xหรือset -o xtrace) จะต้องกำหนดsetเป็นฟังก์ชันส่งออกที่ไม่ทำอะไรเลยเมื่อผ่าน-x(แม้ว่าจะทำลายสคริปต์ถ้ามัน (หรือbashสคริปต์อื่น ๆ ที่มันเรียก) ใช้setเพื่อตั้งค่าพารามิเตอร์ตำแหน่ง)

ชอบ:

set()
  case $1 in
    (-x) return 0;;
    (-[!-]|"") builtin set "$@";;
    (*) echo >&2 That was a bad idea, try something else; builtin set "$@";;
  esac

export -f set
./script.bash

ตัวเลือกอื่นคือการเพิ่มกับดัก DEBUG ใน$BASH_ENVไฟล์ที่ทำset +xก่อนทุกคำสั่ง

echo 'trap "{ set +x; } 2>/dev/null" DEBUG' > ~/.no-xtrace
BASH_ENV=~/.no-xtrace ./script.bash

ที่ไม่ทำงานเมื่อset -xเสร็จใน sub-shell แม้ว่า

ดังที่ @ilkkachu กล่าวว่าหากคุณได้รับอนุญาตให้เขียนไปยังโฟลเดอร์ใด ๆ ในระบบไฟล์อย่างน้อยที่สุดคุณควรจะสามารถทำสำเนาของสคริปต์และแก้ไขได้

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

 bash <(sed 's/set -x/set +x/g' ./script.bash)

นั้น (และวิธีการคัดลอก) อาจทำงานไม่ถูกต้องหากสคริปต์ทำสิ่งที่แฟนซีด้วย$0หรือตัวแปรพิเศษเช่น$BASH_SOURCE(เช่นค้นหาไฟล์ที่สัมพันธ์กับตำแหน่งของสคริปต์เอง) ดังนั้นคุณอาจต้องแก้ไขเพิ่มเติมเช่น แทนที่$0ด้วยเส้นทางของสคริปต์ ...


คำตอบแรกของคุณคือสิ่งที่ฉันต้องการสะอาดและสง่างาม คุณช่วยอธิบายเล็กน้อยว่ามันทำงานอย่างไร ทำไมต้องเป็นเลข 7 ใช้ตัวเลขอื่นอะไรได้บ้าง? ขอบคุณ
คน

@ มนุษย์ดูการแก้ไข
Stéphane Chazelas

1
{BASH_XTRACEFD}>เคล็ดลับการทำงานในbash4.1 หรือภายหลังได้เช่นกัน
chepner

@chepner ใช่คุณสมบัติถูกเพิ่มไปยัง zsh, ksh93 และทุบตีในเวลาเดียวกันตามคำแนะนำโดยนักพัฒนา zsh แต่นี่มันไม่ได้ทำงานksh93หรือbashในการที่ตัวแปรไม่ผ่านในสภาพแวดล้อมของคำสั่ง (เปรียบเทียบ<shell> -c 'export fd; printenv fd {fd}> /dev/null'ในzsh, bashและksh93) คุณสามารถทำให้มันทำงานในksh93/ bashโดยทำมันในสองขั้นตอนหรืออาจจะใช้evalแต่สำหรับbashที่จะมีผลข้างเคียงถ้าตัวเลือก xtrace อยู่ใน
Stéphane Chazelas

5

เนื่องจากเป็นสคริปต์คุณสามารถทำสำเนาและแก้ไขได้

นอกเหนือจากนั้นการกรองผลลัพธ์จะดูง่ายคุณสามารถกำหนดPS4สิ่งที่ผิดปกติได้อย่างชัดเจนมากกว่าเครื่องหมายบวกเพื่อให้การกรองง่ายขึ้น:

PS4="%%%%" bash script.sh 2>&1 | grep -ve '^%%%%'

(แน่นอนว่าจะยุบ stdout และ stdin แต่การวางท่อเพียง stderr ใน Bash จะมีขนดกเล็กน้อยดังนั้นฉันจะไม่สนใจมัน)


1
การวางท่อ stderr ทำได้ง่าย: PS4="%%%%" bash script.sh 2> >(grep -ve '^%%%%').
แพทริค

4
@Patrick การทำเช่นนั้นbashมีปัญหาเนื่องจากgrepมีการเรียกใช้แบบอะซิงโครนัส (ทุบตีไม่รอดังนั้นจึงสามารถ (และมักจะ) ส่งออกสิ่งต่าง ๆ หลังจากคำสั่งถัดไปในสคริปต์เริ่มทำงาน)
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.