echo 및 <<<, 아니면 Bash Award에서 에코를 쓸모 없게 사용합니까?

echo 및 <<<, 아니면 Bash Award에서 에코를 쓸모 없게 사용합니까?

지금까지cat보상은 소용없다잘 알려져 있고 언급되기도 함쓸모없는 사용echo(이 질문과 관련이 없습니다). "Bash에서 쓸모없는 사용 상"이 있어야 하는지 궁금합니다 echo. 일부 매우 비과학적인 측정에 따르면 파이프는 heredocs 및 herestrings보다 훨씬 느린 것 같습니다.

  • 상속 문서:

    for reps in 1 2 3
    do
        time for i in {1..1000}
        do
            cat <<'END'
    test string
    END
        done > /dev/null
    done
    
    real    0m1.786s
    user    0m0.212s
    sys     0m0.332s
    
    real    0m1.817s
    user    0m0.232s
    sys     0m0.332s
    
    real    0m1.846s
    user    0m0.256s
    sys     0m0.320s
    
  • 허스트

    for reps in 1 2 3
    do
        time for i in {1..1000}
        do
            cat <<< 'test string'
        done > /dev/null
    done
    
    real    0m1.932s
    user    0m0.280s
    sys     0m0.288s
    
    real    0m1.956s
    user    0m0.248s
    sys     0m0.348s
    
    real    0m1.968s
    user    0m0.268s
    sys     0m0.324s
    
  • 리디렉션

    for reps in 1 2 3
    do
        time for i in {1..1000}
        do
            echo 'test string' | cat
        done > /dev/null
    done
    
    real    0m3.562s
    user    0m0.416s
    sys     0m0.548s
    
    real    0m3.924s
    user    0m0.384s
    sys     0m0.604s
    
    real    0m3.343s
    user    0m0.400s
    sys     0m0.552s
    

일반적으로 heredocs와 Herestrings의 속도는 거의 같습니다(이는 여러 테스트 중 하나의 데이터 세트에 불과함). 반면 리디렉션은 지속적으로 50% 이상 느립니다. 제가 뭔가 잘못 이해한 것인가요? 아니면 이것이 Bash에서 표준 입력을 읽는 명령에 대한 일반 규칙으로 사용될 수 있습니까?

답변1

먼저, 성능에 중점을 두겠습니다. 나는 데비안 스크레이프를 실행하는 x86_64 프로세서에서 약간 다른 프로그램의 벤치마크를 실행했는데, 그렇지 않으면 대부분 유휴 상태였습니다.

herestring.bash, herestring을 사용하여 입력 줄을 전달합니다.

#! /bin/bash
i=0
while [ $i -lt $1 ]; do
  tr a-z A-Z <<<'hello world'
  i=$((i+1))
done >/dev/null

heredoc.bash, heredoc를 사용하여 입력 행을 전달하십시오.

#! /bin/bash
i=0
while [ $i -lt $1 ]; do
  tr a-z A-Z <<'EOF'
hello world
EOF
  i=$((i+1))
done >/dev/null

echo.bash, echo입력 라인을 전달하려면 및 파이프를 사용하십시오.

#! /bin/bash
i=0
while [ $i -lt $1 ]; do
  echo 'hello world' | tr a-z A-Z
  i=$((i+1))
done >/dev/null

비교를 위해 ATT ksh93 및 대시 아래에서 스크립트의 시간도 측정했습니다( herestring.bash대시에는 이 문자열이 없으므로 제외).

중앙값 트리플은 다음과 같습니다.

$ time bash ./herestring.bash 10000
./herestring.bash 10000  0.32s user 0.79s system 15% cpu 7.088 total
$ time ksh ./herestring.bash 10000
ksh ./herestring.bash 10000  0.54s user 0.41s system 17% cpu 5.277 total
$ time bash ./heredoc.bash 10000
./heredoc.bash 10000  0.35s user 0.75s system 17% cpu 6.406 total
$ time ksh ./heredoc.bash 10000  
ksh ./heredoc.sh 10000  0.54s user 0.44s system 19% cpu 4.925 total
$ time sh ./heredoc.bash 10000  
./heredoc.sh 10000  0.08s user 0.58s system 12% cpu 5.313 total
$ time bash ./echo.bash 10000
./echo.bash 10000  0.36s user 1.40s system 20% cpu 8.641 total
$ time ksh ./echo.bash 10000
ksh ./echo.sh 10000  0.47s user 1.51s system 28% cpu 6.918 total
$ time sh ./echo.sh 10000
./echo.sh 10000  0.07s user 1.00s system 16% cpu 6.463 total

결론적으로:

  • Heredoc은 Herestring보다 빠릅니다.
  • echo파이프라인이 눈에 띄게 빨라졌지만 크게 빠르지는 않습니다. (이것은 장난감 프로그램이라는 점을 기억하십시오. 실제 프로그램에서는 대부분의 처리 시간이 tr여기에 있는 호출에 포함됩니다.)
  • 속도를 원한다면 bash를 버리고 대신 dash 또는 더 나은 ksh를 호출하세요. Bash의 성능은 상대적인 느린 속도를 보완하지 못하지만 ksh는 성능과 속도를 모두 갖추고 있습니다.

성능 외에도 명확성과 이식성이 있습니다. <<<ksh93/bash/zsh 확장이지만 잘 echo … |알려져 있지는 않습니다 <<. ksh88/pdksh 또는 POSIX sh에서는 작동하지 않습니다.

더 명확하다고 할 수 있는 유일한 곳은 <<<구분 기호 내부입니다.

foo=$(tr a-z A-Z <<<'hello world')

비교됨

foo=$(tr a-z A-Z <<'EOF'
hello world
EOF
)

(대부분의 쉘은 이를 포함하는 줄 끝에 있는 대괄호를 처리할 수 없습니다 <<EOF.)

답변2

여기에서 문서를 사용하는 또 다른 이유(문서가 충분하지 않은 경우)는 스트림이 소비되지 않으면 에코가 실패할 수 있다는 것입니다. bash pipefail옵션 사용을 고려해보세요:

set -o pipefail
foo=yawn
echo $foo | /bin/true ; echo $?  # returns 0

/bin/true표준 입력을 사용하지 않지만 echo yawn여전히 완료됩니다. 그러나 echo에 대량의 데이터를 인쇄하도록 요청하면 true다음이 될 때까지 완료되지 않습니다.

foo=$(cat /etc/passwd)
# foo now has a fair amount of data

echo $foo | /bin/true ; echo $?  # returns 0 sometimes 141
echo $foo$foo$foo$foo | /bin/true ; echo $?  # returns mostly 141

141은 SIGPIPE (128 + 13)입니다 (bash가 bash(1)에 따라 이를 수행하기 때문에 128이 추가되었습니다:

명령이 치명적인 신호 N으로 종료되면 bash는 종료 상태로 128+N 값을 사용합니다.

Heredocs에는 이 문제가 없습니다.

/bin/true <<< $foo$foo$foo$foo ; echo $?  # returns 0 always

답변3

echo를 사용하려는 한 가지 이유는 heredocs 및 herestrings 끝에 추가된 개행 문자를 어느 정도 제어할 수 있다는 것입니다.

세 문자 foo의 길이는 3입니다.

$ echo -n foo | wc -c
3

그러나 여기서는 3개의 문자열이 4개의 문자입니다.

$ wc -c <<< foo
4

또한 3자리 구분 기호:

$ wc -c << EOF
foo
EOF
4

네 번째 문자는 개행 문자입니다 0x0a.

어떻게 든 이것은 bash가 서브 쉘에서 출력을 얻을 때 이러한 줄 바꿈을 제거하는 방식에 마술처럼 맞습니다.

foo이는 4개의 문자( 및 ) 를 반환하는 명령입니다 \n. '\n'은 echo에 의해 추가되며, 옵션을 지정하지 않는 한 항상 개행 문자를 추가합니다 -n.

$ echo foo
foo
$ echo foo | wc -c
4

그러나 이를 변수에 할당하면 echo에 의해 추가된 후행 줄 바꿈이 제거됩니다.

$ foo=$(echo foo)
$ echo "$foo" # here, echo adds a newline too.
foo

따라서 파일과 변수를 혼합하여 계산에 사용하는 경우(예: 개행을 추가하기 때문에 heredocs나 herestrings를 사용할 수 없습니다.

foo=abc
echo -n 'abc' > something.txt
if [ $(wc -c <<< "$foo") -eq $(wc -c < something.txt) ] ; then
  echo "yeah, they're the same!"
else
  echo "foo and bar have different lengths (except, maybe not)"
fi

if 문을 읽도록 변경하면

if [ $(echo -n "$foo" | wc -c) -eq $(wc -c < something.txt) ] ; then

그러면 테스트가 통과됩니다.

관련 정보