วิธีการสะท้อนคำสั่งของเชลล์ในขณะที่ดำเนินการ


911

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

ตัวอย่างเช่นกำหนดบรรทัดต่อไปนี้:

ls $DIRNAME

ฉันต้องการให้สคริปต์รันคำสั่งและแสดงต่อไปนี้

ls /full/path/to/some/dir

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

คำตอบ:


1042

set -xหรือset -o xtraceขยายตัวแปรและพิมพ์เครื่องหมาย + เล็กน้อยก่อนถึงบรรทัด

set -vหรือset -o verboseไม่ขยายตัวแปรก่อนพิมพ์

ใช้set +xและset +vเพื่อปิดการตั้งค่าด้านบน

ในบรรทัดแรกของสคริปต์หนึ่งสามารถวาง#!/bin/sh -x(หรือ-v) เพื่อให้มีผลเช่นเดียวกับset -x(หรือ-v) ในภายหลังสคริปต์

/bin/shดังกล่าวข้างต้นยังทำงานร่วมกับ

ดูวิกิพีเดียทุบตีแฮกเกอร์ในsetคุณลักษณะและการแก้จุดบกพร่อง

$ cat shl
#!/bin/bash                                                                     

DIR=/tmp/so
ls $DIR

$ bash -x shl 
+ DIR=/tmp/so
+ ls /tmp/so
$

7
หากคุณต้องการดูว่ามีการดำเนินการหมายเลขบรรทัดใดให้ดูที่stackoverflow.com/a/17805088/1729501
user13107

3
จะทำอย่างไรถ้าฉันต้องการให้สีคำสั่งเมื่อ echoing เพื่อแยกความแตกต่างของคำสั่งและผลลัพธ์ผลลัพธ์?
Lewis Chan

10
จะเป็นอย่างไรถ้าฉันต้องการให้แต่ละคำสั่งผ่านซินธิไซเซอร์เสียงพูดแบบ vocaloid เมื่อมีเสียงก้องดังนั้นฉันจึงได้ยินสิ่งที่กำลังทำอยู่ในระยะไกลและเพลิดเพลินกับเสียงเพลงด้วย? อาจจะเป็นเสียงที่แตกต่างกันสำหรับอินพุตและเอาต์พุต
ADJenks

(ABS มีประวัติอันยาวนานในการไม่ตอบสนองต่อคำขอที่พวกเขาแก้ไขตัวอย่างการปฏิบัติที่ไม่ดีไปยังจุดที่ผลักดันให้สมาชิกชุมชนที่มีมายาวนานสร้างทรัพยากรการแข่งขันย้ายลิงค์ไปยังวิกิ bash-hackers '- the Wooledge wiki คู่มือทุบตีอย่างเป็นทางการก็จะเป็นทรัพยากรที่ดีกว่า)
Charles Duffy

317

set -x จะให้สิ่งที่คุณต้องการ

นี่คือตัวอย่างเชลล์สคริปต์ที่จะสาธิต:

#!/bin/bash
set -x #echo on

ls $PWD

สิ่งนี้จะขยายตัวแปรทั้งหมดและพิมพ์คำสั่งเต็มก่อนที่จะส่งออกคำสั่ง

เอาท์พุท:

+ ls /home/user/
file1.txt file2.txt

21
การใช้คำว่า "verbose" ด้วยวิธีนั้นจะไม่ประสบความสำเร็จ คุณสามารถทำset -o verboseหรือset -v(เฉพาะ "verbose") หรือset -o xtraceหรือset -x(เฉพาะ "xtrace") หรือset -xv(ทั้งสอง) หรือset -o xtrace -o verbose(ทั้งคู่)
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

ใช้งานได้ดี แต่โปรดทราบว่า "verbose" จะเขียนทับ $ 1
JasonS

90

ฉันใช้ฟังก์ชั่นเพื่อสะท้อนและเรียกใช้คำสั่ง:

#!/bin/bash
# Function to display commands
exe() { echo "\$ $@" ; "$@" ; }

exe echo hello world

ซึ่งเอาท์พุท

$ echo hello world
hello world

สำหรับไพพ์คำสั่งที่ซับซ้อน ฯลฯ คุณสามารถใช้ eval:

#!/bin/bash
# Function to display commands
exe() { echo "\$ ${@/eval/}" ; "$@" ; }

exe eval "echo 'Hello, World!' | cut -d ' ' -f1"

ซึ่งเอาท์พุท

$  echo 'Hello, World!' | cut -d ' ' -f1
Hello

โหวตไม่มากสำหรับคำตอบนี้ มีเหตุผลไหมที่เป็นความคิดที่ไม่ดี? ทำงานให้ฉันและดูเหมือนจะเป็นสิ่งที่ฉันกำลังมองหา ...
fru1tbat

1
นี่คือคำตอบที่ดีที่สุดหากคุณไม่ต้องการให้พิมพ์คำสั่งทุกคำสั่ง มันหลีกเลี่ยงการ++ set +xส่งออกเมื่อปิดเช่นเดียวกับการดูสะอาดตา สำหรับคำสั่งเดียวหรือสองคำตอบของ bhassel โดยใช้ subshell นั้นสะดวกที่สุด
Brent Faust

นี่คือสิ่งที่ฉันกำลังมองหา! ไม่set +xมันมีผลกับคำสั่งทั้งหมดซึ่งมากเกินไป!
Hlung

11
ข้อเสียที่สำคัญคือสิ่งนี้คือผลลัพธ์ที่สูญเสียข้อมูลการอ้างอิง คุณไม่สามารถแยกความแตกต่างระหว่างcp "foo bar" bazและcp foo "bar baz"เช่น ดังนั้นจึงเป็นการดีสำหรับการแสดงข้อมูลความคืบหน้าให้กับผู้ใช้ น้อยดังนั้นสำหรับการดีบักเอาต์พุตหรือการบันทึกคำสั่งที่ทำซ้ำได้ กรณีการใช้งานที่แตกต่างกัน ในzshคุณสามารถรักษาการอ้างอิงด้วย:qตัวแก้ไข:exe() { echo '$' "${@:q}" ; "$@" ; }
Andrew Janke

2
ฉันไม่ชอบคำตอบนี้ มีกรณีขอบจำนวนมากที่สิ่งที่คุณเห็นไม่ใช่สิ่งที่คุณได้รับ (โดยเฉพาะอย่างยิ่งกับช่องว่างคำพูดตัวละครที่หลบหนีการแทนที่ตัวแปร / นิพจน์ ฯลฯ ) ดังนั้นอย่าวางคำสั่ง echoed ลงในเทอร์มินัลและคิดว่ามันจะทำงาน วิธีเดียวกัน นอกจากนี้เทคนิคที่สองเป็นเพียงการแฮ็คและจะตัดอินสแตนซ์อื่น ๆ ของคำevalออกจากคำสั่งของคุณ ดังนั้นอย่าคาดหวังว่ามันจะทำงานได้อย่างถูกต้องexe eval "echo 'eval world'"!
mwfearnley

88

นอกจากนี้คุณยังสามารถสลับสิ่งนี้เพื่อเลือกบรรทัดในสคริปต์ของคุณโดยใส่ไว้ในset -xและset +xตัวอย่างเช่น

#!/bin/bash
...
if [[ ! -e $OUT_FILE ]];
then
   echo "grabbing $URL"
   set -x
   curl --fail --noproxy $SERV -s -S $URL -o $OUT_FILE
   set +x
fi

54

คำตอบ shuckc ของสำหรับสะท้อนเลือกสายมีไม่กี่ข้อเสีย: คุณจบลงด้วยดังต่อไปนี้set +xคำสั่งที่ถูกสะท้อนเป็นอย่างดีและคุณจะสูญเสียความสามารถในการทดสอบรหัสทางออกด้วยเนื่องจากมันได้รับการเขียนทับโดย$?set +x

ตัวเลือกอื่นคือการเรียกใช้คำสั่งใน subshell:

echo "getting URL..."
( set -x ; curl -s --fail $URL -o $OUTFILE )

if [ $? -eq 0 ] ; then
    echo "curl failed"
    exit 1
fi

ซึ่งจะให้ผลลัพธ์เช่น:

getting URL...
+ curl -s --fail http://example.com/missing -o /tmp/example
curl failed

สิ่งนี้จะทำให้เกิดค่าใช้จ่ายในการสร้าง subshell ใหม่สำหรับคำสั่ง


7
เป็นวิธีที่ดีในการหลีกเลี่ยง++ set +xเอาต์พุต
Brent Faust

3
ได้ดียิ่งขึ้น: แทนที่ด้วยif [ $? -eq 0 ] if (set -x; COMMAND)
Amedee Van Gasse

35

อีกทางเลือกหนึ่งคือการใส่ "-x" ที่ด้านบนสุดของสคริปต์แทนบรรทัดคำสั่ง:

$ cat ./server
#!/bin/bash -x
ssh user@server

$ ./server
+ ssh user@server
user@server's password: ^C
$

หมายเหตุว่านี้ไม่ได้ดูเหมือนจะทำงานตรงเดียวกันระหว่างและ./myScript bash myScriptยังคงเป็นสิ่งที่ดีที่จะชี้ให้เห็นขอบคุณ
altendky

27

ตามคู่มือ BashของTLDP สำหรับผู้เริ่มต้น: บทที่ 2 การเขียนและการดีบักสคริปต์ :

2.3.1 การดีบักสคริปต์ทั้งหมด

$ bash -x script1.sh

...

ขณะนี้มีการดีบักเต็มเปี่ยมสำหรับทุบตีได้ที่SourceForge คุณสมบัติการแก้ไขข้อบกพร่องเหล่านี้มีอยู่ใน Bash รุ่นทันสมัยส่วนใหญ่เริ่มต้นจาก 3.x

2.3.2 การดีบักส่วนของสคริปต์

set -x            # Activate debugging from here
w
set +x            # Stop debugging from here

...

ตารางที่ 2-1 ภาพรวมของตัวเลือกการตั้งค่าการดีบัก

    Short  | Long notation | Result
    -------+---------------+--------------------------------------------------------------
    set -f | set -o noglob | Disable file name generation using metacharacters (globbing).
    set -v | set -o verbose| Prints shell input lines as they are read.
    set -x | set -o xtrace | Print command traces before executing command.

...

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

#!/bin/bash -xv

18

พิมพ์ "bash -x" บนบรรทัดคำสั่งหน้าชื่อของสคริปต์ Bash ตัวอย่างเช่นหากต้องการดำเนินการ foo.sh ให้พิมพ์:

bash -x foo.sh

11

คุณสามารถรันสคริปต์ทุบตีในโหมดการแก้ปัญหาด้วยตัวเลือก-x

สิ่งนี้จะสะท้อนคำสั่งทั้งหมด

bash -x example_script.sh

# Console output
+ cd /home/user
+ mv text.txt mytext.txt

นอกจากนี้คุณยังสามารถบันทึก -x ตัวเลือกในสคริปต์ เพียงระบุ-xตัวเลือกใน shebang

######## example_script.sh ###################
#!/bin/bash -x

cd /home/user
mv text.txt mytext.txt

##############################################

./example_script.sh

# Console output
+ cd /home/user
+ mv text.txt mytext.txt

2
นอกจากนี้ยังbash -vxจะทำเช่นเดียวกัน แต่ไม่มีการแก้ไขตัวแปร
loa_in_


2

สำหรับcshและtcshคุณสามารถset verboseหรือset echo(หรือคุณสามารถตั้งค่าได้ทั้งคู่ แต่อาจทำให้ซ้ำซ้อนได้เกือบทุกครั้ง)

verboseตัวเลือกพิมพ์สวยมากแสดงออกเปลือกที่แน่นอนที่คุณพิมพ์

echoตัวเลือกมากขึ้นแสดงให้เห็นถึงสิ่งที่จะต้องดำเนินการผ่านการวางไข่


http://www.tcsh.org/tcsh.html/Special_shell_variables.html#verbose

http://www.tcsh.org/tcsh.html/Special_shell_variables.html#echo


Special shell variables

verbose If set, causes the words of each command to be printed, after history substitution (if any). Set by the -v command line option.

echo If set, each command with its arguments is echoed just before it is executed. For non-builtin commands all expansions occur before echoing. Builtin commands are echoed before command and filename substitution, because these substitutions are then done selectively. Set by the -x command line option.


1
$ cat exampleScript.sh
#!/bin/bash
name="karthik";
echo $name;

bash -x exampleScript.sh

เอาท์พุทเป็นดังนี้:

ป้อนคำอธิบายรูปภาพที่นี่


ข้อกำหนดสีสำหรับเทอร์มินัลของคุณคืออะไร (RGB, ตัวเลข)
Peter Mortensen

1

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

ไม่มีหลักฐาน:

exe() { echo "\$ $@" ; "$@" ; }
exe ls -F | grep *.txt

ขาออก:

$
file.txt

ด้วย eval:

exe() { echo "\$ $@" ; "$@" ; }
exe eval 'ls -F | grep *.txt'

ซึ่งเอาท์พุท

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