Làm thế nào tôi có thể có điều kiện vượt qua một subshell thông qua 'thời gian'?


9

Tôi có một tập lệnh thiết lập cho hộp Vagrant nơi tôi đã sử dụng để đo các bước đơn lẻ time. Bây giờ tôi muốn kích hoạt hoặc vô hiệu hóa các phép đo thời gian.

Ví dụ: trước đây một dòng sẽ trông như sau:

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

Bây giờ tôi nghĩ rằng tôi chỉ có thể làm một cái gì đó như thế này:

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

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

Nhưng điều này sẽ không hoạt động:

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

Vấn đề ở đây là gì?


3
Bạn chỉ cần đạt vận tốc mục tiêu để tụ điện hoạt động;)
goldilocks

2
@goldilocks Ý bạn là nam châm ? ;)
một CVn

Câu trả lời:


13

Để có thể đặt thời gian cho một subshell, bạn cần time từ khóa , không phải lệnh.

Các timetừ khóa, một phần của ngôn ngữ, là chỉ được công nhận như vậy khi bước vào nghĩa đen và là từ đầu tiên của một lệnh (và trong trường hợp ksh93, sau đó các dấu hiệu tiếp theo không bắt đầu bằng một -). Ngay cả việc nhập "time"sẽ không hoạt động một mình $TIME(và sẽ được thực hiện như một lệnh gọi timethay thế).

Bạn có thể sử dụng các bí danh ở đây được mở rộng trước khi một vòng phân tích cú pháp khác được thực hiện (vì vậy sẽ để shell nhận ra timetừ khóa đó ):

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)

Các time từ khóa không mất tùy chọn (trừ -ptrong bash), nhưng định dạng này có thể được thiết lập với các TIMEFORMATbiến trong bash. ( shoptphần này cũng đặc bashbiệt, các vỏ khác thường không cần điều đó).


Bạn đã nói "Để có thể đặt thời gian cho một subshell, bạn cần từ khóa thời gian". Tôi tự hỏi hành vi này được ghi nhận ở đâu?
cuonglm

@cuonglm, xeminfo -f bash --index-search=time
Stéphane Chazelas

3

Mặc dù đây aliaslà một cách để làm điều đó, nhưng điều này cũng có thể được thực hiện eval- chỉ là bạn không muốn evalthực thi lệnh như bạn muốn khai báoeval lệnh .

Tôi thích aliases - Tôi sử dụng chúng mọi lúc, nhưng tôi thích các chức năng tốt hơn - đặc biệt là khả năng xử lý các tham số của chúng và chúng không nhất thiết phải được mở rộng ở vị trí lệnh như yêu cầu cho aliases.

Vì vậy, tôi nghĩ có lẽ bạn cũng muốn thử điều này:

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

Các $IFSbit chủ yếu là về $*. Điều quan trọng là các ( subshell bit )cũng là kết quả của một sự mở rộng vỏ và như vậy để mở rộng các đối số vào một chuỗi Tôi sử dụng được phân tách "$*" (không eval "$@", bằng cách này, trừ khi bạn tất cả nhất định đang của args có thể được ghép không gian) . Dấu phân cách giữa các args "$*"là byte đầu tiên $IFSvà do đó có thể nguy hiểm khi tiếp tục mà không chắc chắn giá trị của nó. Vì vậy, các chức năng tiết kiệm $IFS, đặt nó vào một \newline đủ lâu để set ... "$*"vào "$3", unsetlà nó, sau đó reset giá trị của nó nếu nó trước đây có một.

Đây là một bản demo nhỏ:

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

Bạn thấy tôi đặt hai lệnh trong giá trị của $TIMEnó - bất kỳ số nào cũng được - thậm chí không có gì - nhưng hãy chắc chắn rằng nó được thoát và trích dẫn đúng - và các đối số cũng vậy _time(). Tất cả chúng sẽ được nối vào một chuỗi lệnh khi chúng được thực thi - nhưng mỗi đối số có \newline riêng và do đó chúng có thể được trải ra tương đối dễ dàng. Hoặc nếu không, bạn có thể gộp tất cả chúng lại thành một, nếu bạn thích và tách chúng ra trên \newlines hoặc semi-colons hoặc what-have-you. Chỉ cần chắc chắn rằng một đối số duy nhất đại diện cho một lệnh bạn cảm thấy thoải mái khi đặt dòng riêng của nó trong một tập lệnh khi bạn gọi nó. \(, ví dụ là tốt, miễn là cuối cùng nó được theo sau \). Về cơ bản là những thứ bình thường.

Khi evalđược cho ăn đoạn trích trên, nó trông giống như:

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

Và, kết quả của nó trông giống như ...

ĐẦU RA

+++ 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

Các hashlỗi chỉ ra rằng tôi không có một /usr/bin/timecài đặt (bởi vì tôi không)commandchúng ta hãy cho chúng tôi biết thời gian đó đang chạy. Trailing set +xlà một lệnh khác được thực thi sau time (có thể) - điều quan trọng là phải cẩn thận với các lệnh đầu vào khi evaling bất cứ thứ gì.


Bất kỳ lý do cho việc không làm cho nó _time() { eval "$TIME $@"; }? Sử dụng một hàm có nhược điểm là giới thiệu một phạm vi khác cho các biến (không phải là vấn đề đối với các mạng con)
Stéphane Chazelas

@ StéphaneChazelas - vì các trích dẫn trong bản "$@"mở rộng. Tôi thích một cuộc tranh cãi duy nhất eval- nó thật đáng sợ. Ummm .... bạn có ý nghĩa như thế nào về phạm vi khác nhau? Tôi nghĩ rằng đó chỉ là functionchức năng ...
mikeerv 18/12/14

evaltham gia vào các đối số của nó trước khi thực hiện đó là những gì bạn đang cố gắng làm theo một cách rất phức tạp. Tôi có nghĩa là _time 'local var=1; blah'sẽ làm cho varđịa phương đó _time, hoặc _time 'echo "$#"'sẽ in chức năng $#đó _time, không phải của người gọi.
Stéphane Chazelas

@ StéphaneChazelas - Tôi đã hoàn thành hai tab cho bạn ở đây. Phải - evalche giấu lập luận của nó về không gian - như bạn nói, chính xác là những gì tôi làm ở đây - mặc dù đó không phải là ý định bẩm sinh. Tôi sẽ sửa nó. evaling "$*"như trái ngược với "$@"là một thói quen tôi nhặt sau nhiều run-in với command not foundkhi args được tham gia tại địa điểm sai. Trong mọi trường hợp, mặc dù các đối số cuối cùng được thực thi trong hàm, nó đủ đơn giản để mở rộng chúng khi gọi, tôi nghĩ vậy. Đó là những gì tôi sẽ làm với "$#"anyway.
mikeerv

+1 cho đoạn tin tặc xoắn đẹp mắt ...
Stéphane Chazelas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.