스크립트는 shebang "/usr/bin/env sh"에서는 작동하지 않지만 sh는 bash를 가리키지만 "/usr/bin/env bash"에서는 작동합니다.

스크립트는 shebang "/usr/bin/env sh"에서는 작동하지 않지만 sh는 bash를 가리키지만 "/usr/bin/env bash"에서는 작동합니다.

나는 가장 높은 성능을 가진 프로그램을 선택하는 데 도움이 되는 인수로 전달된 다양한 프로그램의 성능을 기록하는 쉘 스크립트를 가지고 있습니다. 관련 스니펫:

#!/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 ...모든 것이 예상대로 작동합니다. 그러나 다음 두 호출은 실패합니다(추가 오류 포함).

  1. perf ...
  2. 아니면 작업 디렉토리에 넣고 다음과 같이 호출할 수도 있습니다../perf ...

여기에 이미지 설명을 입력하세요.

shebang을 변경하면 /usr/bin/env bash문제가 해결되었지만 내 시스템 /usr/bin/sh에 문제가 발생했습니다 ./usr/bin/bash

답변1

sh스크립트를 인터프리터로 실행하고 있지만 bash함수를 사용하고 있습니다. 그것이 그것을 가리킨다고 가정하더라도 sh, 실행하라는 요청을 받을 때 많은 사용자 정의 기능이 비활성화되기 bash때문에 여전히 이것을 할 수 없습니다 .bashsh

한 가지 해결책은 스크립트를 올바르게 선언하는 것입니다. 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 모드에서도 간접 확장을 지원하지만 여기서는 더 표준을 준수하는 방식으로 이 코너 케이스를 처리합니다. 이것문서에는 실제로 이렇게 나와 있습니다.:

  1. 변수 간접 참조가 가능하지만 "#" 및 "?" 특수 매개변수에서는 작동하지 않을 수 있습니다.

이제 $!마지막 백그라운드 프로세스의 PID가 포함되어 있지만 스크립트가 어떤 프로세스도 시작하지 않았으므로 표준 의미는 빈 문자열만 제공합니다. 결국 오류가 발생합니다.

"${@: -1}"Bash에서 마지막 위치 인수를 얻는 데 사용할 수도 있습니다 . 또한 표준은 아니지만 POSIX 모드에서 작동합니다.

어쨌든 일반적으로 Bash가 지원하는 POSIX 이외의 기능(예 : (( .. )),, run , not ) 을 사용하려는 경우 . 비호환성 문제를 처리하는 번거로움을 덜 수 있습니다.${!var}${var:n:m}bashsh

관련 정보