Bash set + x โดยไม่ต้องพิมพ์


96

มีใครรู้บ้างว่าเราสามารถพูดได้set +xโดยไม่ต้องพิมพ์:

set -x
command
set +x

ร่องรอย

+ command
+ set +x

แต่ควรพิมพ์

+ command

Bash คือเวอร์ชัน 4.1.10 (4) นี่เป็นปัญหาของฉันมาระยะหนึ่งแล้ว - เอาต์พุตเต็มไปด้วยset +xเส้นที่ไร้ประโยชน์ทำให้เครื่องมือติดตามไม่เป็นประโยชน์เท่าที่ควร


สิ่งนี้ไม่ตอบคำถามของคุณ แต่เมื่อคุณเรียกใช้สคริปต์ทำไมไม่:script.sh 2>&1 | grep -v 'set +x'
cdarke

คำตอบ:


149

ฉันมีปัญหาเดียวกันและฉันสามารถหาวิธีแก้ปัญหาที่ไม่ใช้ subshell:

set -x
command
{ set +x; } 2>/dev/null

10
คำตอบที่ดีเพียงหมายเหตุ: หากไม่มีเครื่องหมายอัฒภาคหลังคำสั่งสิ่งนี้จะไม่ทำงาน และด้วยอัฒภาค แต่ไม่มีช่องว่างให้กับวงเล็บปีกกาข้อผิดพลาดทางไวยากรณ์จะเพิ่มขึ้น
sdaau

9
สิ่งนี้ทำให้สถานะการออกเป็นศูนย์
Garth Kidd

8
@GarthKidd สถานะการออกเป็นศูนย์ทุกคำสั่งที่สำเร็จ set +xเป็นคำสั่งที่ประสบความสำเร็จ
Daniel Alder

4
ในกรณีที่คุณต้องออกจากสถานะของการเปลี่ยนแปลงนี้เป็นวิธีแก้ปัญหา:command { STATUS=$?; set +x; } 2>/dev/nullจากนั้นตรวจสอบ$STATUSในบรรทัดถัดไปตามอัธยาศัย
Greg Price

5
{ set +x; } 2>&-แยกกันนี่เป็นรุ่นที่แข็งแรงเล่นกอล์ฟเล็กน้อยเพิ่มเติม: ที่ปิด fd 2 ทันทีแทนที่จะทำให้ชี้ไปที่ / dev / null บางโปรแกรมไม่สามารถจัดการได้ดีเมื่อพยายามพิมพ์ไปยัง stderr ซึ่งเป็นเหตุผลว่าทำไม / dev / null จึงเป็นรูปแบบที่ดีโดยทั่วไป แต่การset -xติดตามของเชลล์จัดการได้ดีดังนั้นจึงทำงานได้อย่างสมบูรณ์ที่นี่และทำให้คาถานี้สั้นลงเล็กน้อย
Greg Price

43

คุณสามารถใช้ subshell เมื่อออกจาก subshell การตั้งค่าที่xจะหายไป:

( set -x ; command )

ขอบคุณ ... อันที่จริงฉันตระหนักถึง "เคล็ดลับเปลือกย่อย" ฉันหวังว่ามันจะง่ายกว่านั้น มันเกี่ยวข้องกับการเปลี่ยนแปลงที่สำคัญในโค้ดทำให้โค้ดซับซ้อนขึ้นและอ่านได้น้อยลง IMHO ที่จะแย่ไปกว่าการอยู่กับ set + x lines ...
Andreas Spindler

ฉันไม่เห็นวิธีการ( set -x \n command \n )ใด ๆ set -x \n command \n set +xที่เลวร้ายยิ่งกว่า
chepner

3
@chepner: คุณไม่สามารถตั้งค่าตัวแปรได้
choroba

2
... และคุณทำไม่ได้cd: จะไม่เปลี่ยนไดเร็กทอรีปัจจุบันในพาเรนต์เชลล์
Andreas Spindler

ขออภัยฉันไม่แน่ใจว่าฉันคิดว่าการคัดค้านที่แท้จริงของคุณคืออะไร เป็นสัปดาห์ที่ยาวนาน ...
chepner

8

ฉันเพิ่งแฮ็ควิธีแก้ปัญหานี้เมื่อไม่นานมานี้เมื่อฉันรู้สึกรำคาญกับมัน:

shopt -s expand_aliases
_xtrace() {
    case $1 in
        on) set -x ;;
        off) set +x ;;
    esac
}
alias xtrace='{ _xtrace $(cat); } 2>/dev/null <<<'

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

xtrace on
ARG1=$1
ARG2=$2
xtrace off

และคุณจะได้ผลลัพธ์ที่ดูเหมือน:

$ ./script.sh one two
+ ARG1=one
+ ARG2=two

เคล็ดลับที่ชาญฉลาด (แม้ว่าคุณจะไม่ต้องการ/dev/stdinส่วนนั้นก็ตาม) ข้อแม้คือการเปิดการขยายนามแฝงในสคริปต์อาจมีผลข้างเคียงที่ไม่ต้องการ
mklement0

คุณถูก. /dev/stdinฉันได้แก้ไขคำตอบในการลบฟุ่มเฟือย ฉันไม่ทราบถึงผลข้างเคียงใด ๆ เนื่องจากสภาพแวดล้อมที่ไม่โต้ตอบไม่ควรโหลดไฟล์ใด ๆ ที่กำหนดนามแฝง อาจมีผลข้างเคียงอะไรบ้าง?
user108471

1
นั่นเป็นจุดที่ดี - ฉันลืมไปว่านามแฝงไม่ได้รับการถ่ายทอดมาดังนั้นความเสี่ยงจึงน้อยกว่าที่ฉันคิดไว้มาก (สมมุติว่าสคริปต์ของคุณอาจจัดหารหัสของบุคคลที่สามที่เกิดขึ้นเพื่อกำหนดนามแฝง แต่ฉันยอมรับว่านั่นอาจไม่ใช่ของจริง - ความห่วงใยของโลก). +1
mklement0

1
@AndreasSpindler คุณช่วยอธิบายให้ละเอียดได้ไหมว่าทำไมคุณถึงคิดว่าเทคนิคนี้มีแนวโน้มที่จะรั่วไหลข้อมูลที่ละเอียดอ่อนมากขึ้น?
user108471

1
... โดยพื้นฐานแล้วเนื่องจากนามแฝงและฟังก์ชันเป็นโครงสร้างระดับสูง set +xยากกว่ามาก (ถ้าไม่ทำไม่ได้) ที่จะประนีประนอม แต่ก็ยังเป็นทางออกที่ดีและตรงประเด็น - อาจเป็นวิธีที่ดีที่สุดจนถึงตอนนี้
Andreas Spindler

7

วิธีแก้ปัญหาโดยใช้ @ user108471 เวอร์ชันที่เรียบง่าย:

shopt -s expand_aliases
alias trace_on='set -x'
alias trace_off='{ set +x; } 2>/dev/null'

trace_on
...stuff...
trace_off

แล้วเรื่องนี้ล่ะ? function () { set_plus_x='{ set +x; } 2>/dev/null' }
LexH

1

นี่คือการรวมกันของแนวคิดบางอย่างที่สามารถใส่บล็อกโค้ดและรักษาสถานะการออก

#!/bin/bash
shopt -s expand_aliases
alias trace_on='set -x'
alias trace_off='{ PREV_STATUS=$? ; set +x; } 2>/dev/null; (exit $PREV_STATUS)'

trace_on
echo hello
trace_off
echo "status: $?"

trace_on
(exit 56)
trace_off
echo "status: $?"

เมื่อดำเนินการ:

$ ./test.sh 
+ echo hello
hello
status: 0
+ exit 56
status: 56

ความพยายามที่ดี แต่ฉันคิดว่า()รอบ ๆexitไม่จำเป็น ตกลง. อาจจะเป็นเรื่องที่น่าหวาดระแวง แต่หากใช้รหัสนี้โดยทั่วไปคุณมีเวกเตอร์การโจมตีที่ดี: กำหนดนิยามใหม่trace_onและtrace_offและฉีดรหัสที่อ่านคำสั่งที่ดำเนินการ หากคุณใช้ "utilties" ดังกล่าวเพียงอย่างเดียวจะเป็นการให้คำแนะนำ แต่หากใช้โค้ดกับผู้อื่นคุณต้องพิจารณาว่าประโยชน์ของฟังก์ชันที่ไม่ได้มาตรฐานดังกล่าวมีมากกว่าข้อเสียหรือไม่ โดยส่วนตัวแล้วฉันได้ตัดสินด้วย{ set +x; } 2>/dev/nullเนื่องจากโครงสร้างนี้เป็นที่เข้าใจกันทั่วไปและก็ไม่เปลี่ยนสถานะการออก
Andreas Spindler

ขอบคุณ. ฉันได้แบ่งปันวิธีการนี้กับเพื่อนร่วมงานของฉันและพวกเขาชอบที่จะให้ทั้งเอาต์พุตและโค้ดมีเสียงดังน้อยลง เกี่ยวกับ()รอบexit 56; นั่นเป็นเพียงเพื่อให้ฉันสามารถแสดงให้เห็นว่าสถานะการออกของกระบวนการย่อยสามารถเข้าถึงได้ในสคริปต์ทุบตี หากไม่มี parens มันจะไม่กลายเป็นกระบวนการย่อยและสคริปต์จะออกจากจุดนั้นด้วยสถานะ = 65
user3286792
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.