나는 가장 높은 성능을 가진 프로그램을 선택하는 데 도움이 되는 인수로 전달된 다양한 프로그램의 성능을 기록하는 쉘 스크립트를 가지고 있습니다. 관련 스니펫:
#!/usr/bin/env sh
function cal_perf () {
real_t=0
user_t=0
sys_t=0
for (( trial=1 ; trial<=$3 ; ++trial )) ; do
shopt -s lastpipe
/usr/bin/time -f '%e:%S:%U' $1 $2 |& IFS=":" read real sys user
real_t=$(echo "$real + $real_t" | bc)
user_t=$(echo "$user + $user_t" | bc)
sys_t=$(echo "$sys + $sys_t" | bc)
done
real_t=$(echo "scale=2 ; $real_t / $3" | bc)
user_t=$(echo "scale=2 ; $user_t / $3" | bc)
sys_t=$(echo "scale=2 ; $sys_t / $3" | bc)
printf "%s\t%d\t%.2f\t%.2f\t%.2f\n" $2 $3 $real_t $user_t $sys_t >> timings_$(date '+%Y%m%d')
}
# main
printf "program\t#trials\treal_time_am\tuser_time_am\tsys time_am\n" > timings_$(date '+%Y%m%d')
translator=$1
shift
while [ $# -gt 1 ] ; do cal_perf $translator $1 ${!#} ; shift ; done
다음과 같이 명령줄에서 실행해야 합니다.
perf <translator_progam> <list_of_programs_to_compare> <number_of_trials>
xip.py, foo.py, bar.py, bas.py, qux.py
...예를 들어, 작업 디렉터리의 순 콘텐츠 성능을 비교하고 통계를 생성하기 전에 이를 50번씩 실행하고 싶다고 가정해 보겠습니다 .
perf python *py 50
여기서는 뭔가 분명한 것이 빠진 것 같지만 이 스크립트를 호출하면 bash $HOME/bin/perf ...
모든 것이 예상대로 작동합니다. 그러나 다음 두 호출은 실패합니다(추가 오류 포함).
perf ...
- 아니면 작업 디렉토리에 넣고 다음과 같이 호출할 수도 있습니다.
./perf ...
shebang을 변경하면 /usr/bin/env bash
문제가 해결되었지만 내 시스템 /usr/bin/sh
에 문제가 발생했습니다 ./usr/bin/bash
답변1
sh
스크립트를 인터프리터로 실행하고 있지만 bash
함수를 사용하고 있습니다. 그것이 그것을 가리킨다고 가정하더라도 sh
, 실행하라는 요청을 받을 때 많은 사용자 정의 기능이 비활성화되기 bash
때문에 여전히 이것을 할 수 없습니다 .bash
sh
한 가지 해결책은 스크립트를 올바르게 선언하는 것입니다. bash
함수를 사용하고 있으므로 이를 bash
스크립트로 선언하세요.
#!/bin/bash
env
또는 계속해서 의존 해야 하는 경우
#!/usr/bin/env bash
또 다른 옵션은 bash
특정 구문을 제거하는 것입니다.
두 경우 모두 변수를 사용할 때 큰따옴표로 묶어야 한다는 점을 잊지 마세요. 예를 들어, 따옴표가 없는 변수(스크립트 인수 포함) 중 하나에 공백이 포함되어 있으면 쉘은 이를 두 개 이상의 단어로 분할합니다.
답변2
$3
빈 문자열로 확장하면 오류가 발생합니다.
for (( trial=1 ; trial<=$3 ; ++trial ))
불다하다이는 POSIX 모드에서도 지원되므로 for (( .. ))
그 자체로는 문제가 되지 않습니다. 이 특별한 경우 문제는 ${!#}
함수를 호출할 때 사용되는 확장에 있습니다. 두 가지 방법으로 구문 분석할 수 있습니다.
${var#word}
표준 셸에서는 특수 매개변수에 적용되는 접두사 제거 작업 !
으로 빈 문자열을 제거합니다. 빈 문자열을 제거하면 아무 일도 일어나지 않지만, 예를 들어 문자를 제거할 수 있습니다:
$ dash -c 'sleep 123 & echo $! ${!#?};'
27920 7920
그러나 Bash에서는 "포인터" 로 사용되는 ${!#}
간접 확장으로 처리되어 마지막 위치 인수를 제공합니다.${!var}
$#
Bash는 POSIX 모드에서도 간접 확장을 지원하지만 여기서는 더 표준을 준수하는 방식으로 이 코너 케이스를 처리합니다. 이것문서에는 실제로 이렇게 나와 있습니다.:
- 변수 간접 참조가 가능하지만 "#" 및 "?" 특수 매개변수에서는 작동하지 않을 수 있습니다.
이제 $!
마지막 백그라운드 프로세스의 PID가 포함되어 있지만 스크립트가 어떤 프로세스도 시작하지 않았으므로 표준 의미는 빈 문자열만 제공합니다. 결국 오류가 발생합니다.
"${@: -1}"
Bash에서 마지막 위치 인수를 얻는 데 사용할 수도 있습니다 . 또한 표준은 아니지만 POSIX 모드에서 작동합니다.
어쨌든 일반적으로 Bash가 지원하는 POSIX 이외의 기능(예 : (( .. ))
,, run , not ) 을 사용하려는 경우 . 비호환성 문제를 처리하는 번거로움을 덜 수 있습니다.${!var}
${var:n:m}
bash
sh