ฉันจะผ่านการ subshell ผ่าน 'time' แบบมีเงื่อนไขได้อย่างไร


9

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

ตัวอย่างเช่นก่อนหน้านี้บรรทัดจะมีลักษณะดังนี้:

time (apt-get update > /tmp/last.log 2>&1)

ตอนนี้ฉันคิดว่าฉันสามารถทำสิ่งนี้ได้ง่ายๆ:

MEASURE_TIME=true
[[ $MEASURE_TIME = true ]] && TIME="time --format=%e" || TIME=""

$TIME (apt-get update > /tmp/last.log 2>&1)

แต่สิ่งนี้จะไม่ทำงาน:

syntax error near unexpected token `apt-get'
`$TIME (apt-get update > /tmp/last.log 2>&1)'

มีปัญหาอะไรที่นี่


3
คุณเพียงแค่ต้องไปถึงความเร็วเป้าหมายเพื่อให้ตัวเก็บประจุฟลักซ์ทำงาน;)
goldilocks

2
@goldilocks คุณหมายถึงแม่เหล็กหรือไม่ ;)
CVn

คำตอบ:


13

เพื่อให้สามารถกำหนดเวลาใน subshell ได้คุณจำเป็นต้องใช้time คีย์เวิร์ดไม่ใช่คำสั่ง

timeคำหลักส่วนหนึ่งของภาษาที่ได้รับการยอมรับเป็นเพียงดังกล่าวเมื่อป้อนตัวอักษรและเป็นคำแรกของคำสั่ง (และในกรณีของksh93แล้วโทเค็นต่อไปไม่ได้เริ่มต้นด้วย-) แม้การป้อน"time"จะไม่ทำงานอย่างโดดเดี่ยว$TIME(และจะถูกเรียกเป็นการเรียกtimeคำสั่งแทน)

คุณสามารถใช้นามแฝงที่นี่ซึ่งมีการขยายก่อนที่จะทำการแยกวิเคราะห์อีกรอบหนึ่ง (ดังนั้นให้เชลล์รู้จักtimeคำหลักนั้น):

shopt -s expand_aliases
alias time_or_not=
TIMEFORMAT=%E

MEASURE_TIME=true
[[ $MEASURE_TIME = true ]] && alias time_or_not=time

time_or_not (apt-get update > /tmp/last.log 2>&1)

time คำหลักที่ไม่ใช้ตัวเลือก (ยกเว้น-pในbash) แต่รูปแบบที่สามารถตั้งค่ากับตัวแปรในTIMEFORMAT bash( shoptส่วนนี้เป็นแบบbashเฉพาะเจาะจงเชลล์อื่น ๆ โดยทั่วไปไม่จำเป็นต้องใช้)


คุณพูดว่า "เพื่อให้สามารถกำหนดช่วงเวลาย่อยคุณต้องใช้คำหลักเวลา" ฉันสงสัยว่าพฤติกรรมนี้บันทึกไว้ที่ไหนบ้าง?
cuonglm

@cuonglm, ดูinfo -f bash --index-search=time
Stéphane Chazelas

3

ในขณะที่ a aliasเป็นวิธีหนึ่งในการทำสิ่งนี้สามารถทำได้ด้วยevalเช่นกัน - เป็นเพียงการที่คุณไม่ต้องการevalดำเนินการคำสั่งมากเท่าที่คุณต้องการในevalการประกาศคำสั่งประกาศ

ฉันชอบaliases - ฉันใช้ 'em ตลอดเวลา แต่ฉันชอบฟังก์ชั่นที่ดีกว่า - โดยเฉพาะความสามารถในการจัดการพารามิเตอร์และพวกเขาไม่จำเป็นต้องขยายในตำแหน่งคำสั่งตามที่ต้องการสำหรับaliases

ดังนั้นฉันคิดว่าบางทีคุณอาจต้องการลองนี้เช่นกัน:

_time() if   set -- "${IFS+IFS=\$2;}" "$IFS" "$@" && IFS='
';      then set -- "$1" "$2" "$*"; unset IFS
             eval "$1 $TIME ${3#"$1"?"$2"?}"
        fi

บิตส่วนใหญ่เกี่ยวกับ$IFS $*เป็นสิ่งสำคัญที่( subshell bit )เป็นผลมาจากการขยายตัวของเชลล์และเพื่อที่จะขยายข้อโต้แย้งลงในสตริงที่แยกวิเคราะห์ฉันใช้"$*" (ไม่eval "$@", โดยวิธีการเว้นแต่คุณจะแน่ใจว่าทุก args สามารถเข้าร่วมในช่องว่าง) . ตัวคั่นที่แยกระหว่าง args in "$*"เป็นไบต์แรกใน$IFSดังนั้นจึงอาจเป็นอันตรายต่อการดำเนินการโดยไม่ทำให้ค่าของมันแน่นอน ดังนั้นฟังก์ชั่นบันทึก$IFSชุดไป\newline นานพอที่จะset ... "$*"เข้ามา"$3",unset s มันแล้วรีเซ็ตค่าของมันถ้ามันก่อนหน้านี้มีหนึ่ง

นี่คือตัวอย่างเล็ก ๆ น้อย ๆ :

TIME='set -x; time'
_time \( 'echo "$(echo any number of subshells)"' \
         'command -V time'                        \
         'hash time'                              \
      \) 'set +x'

คุณเห็นฉันใส่สองคำสั่งในค่าของ$TIMEที่นั่น - หมายเลขใดดี - แม้ไม่มี - แต่ต้องแน่ใจว่ามันจะหนีออกมาและยกมาอย่างถูกต้อง - _time()และเดียวกันจะไปสำหรับข้อโต้แย้งที่จะ พวกเขาทั้งหมดจะถูกรวมเข้าด้วยกันเป็นสตริงคำสั่งเดียวเมื่อพวกเขาถูกดำเนินการ - แต่แต่ละหาเรื่องได้รับ\newline ของตัวเองและเพื่อให้พวกเขาสามารถแพร่กระจายออกได้อย่างง่ายดาย มิฉะนั้นคุณสามารถรวมมันทั้งหมดในที่เดียวหากคุณต้องการและแยกพวกมันออกเป็น\newlines หรือ semi-colons หรือ what-have-you เพียงแค่ให้แน่ใจว่าอาร์กิวเมนต์เดียวแสดงถึงคำสั่งที่คุณรู้สึกสบายที่จะใส่ในบรรทัดของมันเองในสคริปต์เมื่อคุณเรียกมัน ยกตัวอย่างเช่นจะปรับตราบเท่าที่มันจะตามมาด้วยในที่สุด\( \)โดยทั่วไปสิ่งปกติ

เมื่อevalได้รับตัวอย่างข้างต้นที่เลี้ยงมันดูเหมือนว่า:

+ eval 'IFS=$2;set -x; time (
echo "$(echo any number of subshells)"
command -V time
hash time
)
set +x'

และผลลัพธ์ดูเหมือน ...

เอาท์พุท

+++ echo any number of subshells
++ echo 'any number of subshells'
any number of subshells
++ command -V time
time is a shell keyword
++ hash time
bash: hash: time: not found

real    0m0.003s
user    0m0.000s
sys     0m0.000s
++ set +x

hashข้อผิดพลาดแสดงให้เห็นว่าผมไม่ได้มีการ/usr/bin/timeติดตั้ง(เพราะฉันทำไม่ได้)และcommandขอให้เราทราบว่าเวลาทำงาน การติดตามset +xเป็นคำสั่งอื่นที่ดำเนินการหลังจาก time (ซึ่งเป็นไปได้) - มันเป็นสิ่งสำคัญที่จะต้องระมัดระวังกับคำสั่งอินพุตเมื่อทำการevalอะไร


มีเหตุผลใดที่จะไม่ทำมัน_time() { eval "$TIME $@"; }? การใช้ฟังก์ชั่นมีข้อเสียเปรียบของการแนะนำขอบเขตที่แตกต่างกันสำหรับตัวแปร (ไม่ใช่ปัญหาสำหรับ subshells)
Stéphane Chazelas

@ StéphaneChazelas - เนื่องจากราคาในการ"$@"ขยาย ฉันชอบอาร์คเพียงครั้งเดียวeval- มันน่ากลัวมาก อืม .... คุณหมายถึงเกี่ยวกับขอบเขตที่แตกต่างกันอย่างไร ฉันคิดว่ามันเป็นแค่functionฟังก์ชั่น ...
mikeserv

evalเข้าร่วม args ก่อนที่จะดำเนินการซึ่งเป็นสิ่งที่คุณพยายามทำในลักษณะที่ซับซ้อนมาก ฉันหมายความว่า_time 'local var=1; blah'จะทำให้varท้องถิ่นนั้น_timeหรือที่_time 'echo "$#"'จะพิมพ์$#ของ_timeฟังก์ชั่นที่ไม่ของผู้โทร
Stéphane Chazelas

@ StéphaneChazelas - ฉันมีแท็บสองรายการให้คุณที่นี่ ถูกต้อง - evalconcats มันเกิดขึ้นในช่องว่าง - ซึ่งก็คืออย่างที่คุณพูดสิ่งที่ฉันทำที่นี่ - แม้ว่ามันจะไม่ได้ตั้งใจเจตนา ฉันจะแก้ไขมัน evalไอเอ็นจี"$*"เมื่อเทียบกับ"$@"เป็นนิสัยฉันหยิบขึ้นมาหลังจากที่หลายคนทำงานด้วยcommand not foundเมื่อ args ได้เข้าร่วมที่ผิด ไม่ว่าในกรณีใด ๆ แม้ว่า args จะถูกดำเนินการในท้ายที่สุดในฟังก์ชั่น แต่มันก็ง่ายพอที่จะขยายพวกมันออกมาเมื่อร้องขอผมคิดว่า มันเป็นสิ่งที่ฉันจะทำอย่างไรกับ"$#"ต่อไป
mikeserv

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