표준 출력과 표준 오류 출력 출력을 셸 스크립트로 바꾸기

14656 단어 shelltech

컨디션


다음은 필자가 이 글을 집필할 때의 환경이다.
  • OS
  • Arch Linux 5.11.7-arch1-1
  • shell
  • bash - 5.1.4(1)-release (x86_64-pc-linux-gnu)
  • zsh - 5.8 (x86_64-pc-linux-gnu)
  • fish - 3.1.2
  • 문제 설정


    갑자기 이런 의문이 치밀어 올랐다."셸 스크립트에서 어떤 명령의 표준 출력과 표준 오류 출력 사이를 교환한 후에 다음 명령을 파이프 처리할 수 있습니까?"
    표준 출력과 표준 오류 출력에 각각 출력하는 명령이 있습니다.
    이후 명령 실행에는 명령을 직접 실행할 때, 표준 오류 출력을 버릴 때, 표준 출력을 버릴 때의 3가지 모드가 기재된다.기본적으로 표준 출력과 표준 오류 출력은 현재 연결된 단말기입니다. 그렇지 않으면 표준 출력으로 출력할지 표준 오류 출력으로 출력할지 판단할 수 없습니다.표준 오류 출력을 버리면 터미널 출력이 표준 출력이라고 판단할 수 있습니다.반대로 표준 출력을 버리면 터미널에 출력된 것이 표준 오류 출력이라고 판단할 수 있다.
    # 標準出力と標準エラー出力にそれぞれ出力するコマンド
    stdout_and_stderr () {
      echo 'to stdout'
      echo 'to stderr' >&2
    }
    
    $ stdout_and_stderr
    to stdout
    to stderr
    
    $ stdout_and_stderr 2>/dev/null
    to stdout
    
    $ stdout_and_stderr >/dev/null
    to stderr
    
    이렇게 표준 출력으로 출력된 내용을 표준 오류 출력으로 출력하고, 반대로 표준 오류 출력으로 출력된 내용을 표준 출력으로 출력한다.
    # stdout_and_stderrの標準出力を標準エラー出力に、標準エラー出力を標準出力に出力するコマンド
    inverse_stdout_and_stderr () {
      echo 'to stdout' >&2
      echo 'to stderr'
    }
    
    $ inverse_stdout_and_stderr
    to stdout
    to stderr
    
    $ inverse_stdout_and_stderr 2>/dev/null
    to stderr
    
    $ inverse_stdout_and_stderr >/dev/null
    to stdout
    
    물론 각자의 함수를 이렇게 정의하는 것이 아니라 그 중 하나를 정의한 다음에 다른 명령을 통해 변경한다.

    해결책


    결론적으로 다음 케이스 함수에 표준 출력과 표준 오류 출력을 바꾸려는 명령을 보낼 수 있다.
    # bash or zsh
    stdswap () {
      eval "$@" 3>&2 2>&1 1>&3 3>&-
    }
    
    # fish
    function stdswap
      eval "$argv" 3>&2 2>&1 1>&3 3>&-
    end
    
    이 껍질 함수는 다음과 같다.
    $ stdswap stdout_and_stderr
    to stdout
    to stderr
    
    $ stdswap stdout_and_stderr 2>/dev/null
    to stderr
    
    $ stdswap stdout_and_stderr >/dev/null
    to stdout
    
    $ stdswap inverse_stdout_and_stderr
    to stdout
    to stderr
    
    $ stdswap inverse_stdout_and_stderr 2>/dev/null
    to stdout
    
    $ stdswap inverse_stdout_and_stderr >/dev/null
    to stderr
    
    이것은 상기 교체 전 명령의 출력과 비교하면 표준 출력과 표준 오류 출력이 서로 바뀌었다는 것을 확실히 알 수 있다.

    보태다


    그렇다면 상술한 조개 함수가 무엇을 하고 있는지 살펴보자.
    이 함수를 구성하는 요소는 다음과 같은 두 가지가 있다.
  • eval
  • redirect
  • eval

    eval 매개 변수를 받아들여 케이스로 해석한다."$@"에서 케이스 함수에 건네주는 모든 매개 변수를 얻을 수 있습니다.따라서 명령을 케이스 함수에 매개 변수로 전달할 수 있다.

    redirect


    redirect에 관해서는 조개껍질의 기본적인 용법이지만 평소에 흔히 볼 수 없는 용법이므로 주의해야 한다.상기 조개 함수에서 Redirect를 사용하는 곳은3>&2 2>&1 1>&3 3>&-이다.다만, 이것에 관해서는 아래의 블로그 보도 때문에 상세하게 그쪽을 참조하세요.
    https://fhiyo.github.io/2017/09/12/consecutive-shell-command-redirection.html
    대략적으로 설명하자면, 이것은 임시 변수를 사용하는 값의 교환과 비슷하다.임시 파일 설명자 대신 임시 변수를 사용하여 표준 출력의 파일 설명자와 표준 오류 출력의 파일 설명자를 대체합니다.
    또 위 블로그에서는 사용하지 않았다3>&-.이것은 파일 설명자3를 닫는 작업입니다.
    셸 스크립트로 임시 변수의 값을 교환하면 이렇게 된다고 기술합니다.
    a=1
    b=2
    
    tmp=$a
    a=$b
    b=$tmp
    unset tmp
    

    잡담


    그렇다면 문득 떠오르는 의문을 해소해 보았는데 활용할 만한 정황이 있을까.솔직히 별거 아닌 것 같아.또 위에서 참고한 블로그에서는 해당 이용 주소를 바꿔야 한다고 적힌 사이트를 찾지 못했다.
    하지만 방법을 생각해 봤어요.이것은 time 명령과 조합된 것입니다.즉zsh의time명령이다.나는 다른 조개껍질을 모른다.
    그럼, 나는 time 명령으로 zsh의 시작 시간을 측정할 것이다.이때 몇 차례 측정하여 그 평균치를 시작 시간으로 한다.이를 실현하기 위해 time 명령의 출력을 파이프를 통해 다른 명령에 전달하고 평균 시간을 계산한다.zsh time 명령은 측정 시간을 표준 오류 출력으로 출력합니다.따라서 표준 출력으로 방향을 바꾸는 기초 위에서 평균 시간 측정용 명령에 연결한다.그리고 표준 출력과 표준 오류를 교체해서 이 처리를 실현한다.예를 들어, 이렇게 합니다.
    # on zsh
    
    # zshの起動時間をn回測定
    zt () {
      local -r cmd='time (zsh -i -c "exit" 2>/dev/null)'
      local -ri num=${1}
    
      repeat "$num"; do
        sleep 1
        eval "$cmd"
      done
    }
    
    # パイプされた入力を標準出力に出力しつつ同時に加工する
    teepipe () {
      tee >(eval "$@")
    }
    
    # 平均値計算
    ave_time () {
      cat | awk '{s += $1; c += 1} END {printf "\n  AVG: %f second\n", s/c}'
    }
    
    # zshの起動時間とその平均値の測定
    zshtimes () {
      stdswap zt 10 | teepipe "cut -d ' ' -f 9 | ave_time"
    }
    
    $ zshtimes 10
      0.01s user 0.00s system 97% cpu 0.018 total
      0.01s user 0.01s system 97% cpu 0.018 total
      0.01s user 0.00s system 97% cpu 0.018 total
      0.01s user 0.01s system 97% cpu 0.018 total
      0.01s user 0.01s system 97% cpu 0.018 total
      0.01s user 0.01s system 97% cpu 0.018 total
      0.01s user 0.01s system 97% cpu 0.018 total
      0.02s user 0.00s system 97% cpu 0.018 total
      0.01s user 0.00s system 97% cpu 0.018 total
      0.01s user 0.00s system 98% cpu 0.018 total
    
      AVG: 0.018000 second
    
    단, 본 예에서 보듯이 출력이 없거나 거의 없는 명령의 시간을 측정하려면 표준 출력을 버리는 토대에서 표준 오류 출력을 표준 출력으로 재정비하고 파이프를 통해 측정용 명령으로 전달하면 된다.특별히 표준 출력과 표준 오류 출력을 교체할 필요가 없다.
    그러나 대량 출력 지령에 대한 측정은 이런 교체가 필요하다.출력을 버리면 실제 출력된 부분을 정확하게 측정할 수 없습니다.그러나 여기서 말한 정확한 측정은 time some_command이 측정한 시간과 같다는 뜻이다.출력을 버리면 측정 시간이 달라진다는 것이다.그러면 구체적인 예를 들어 출력을 버리고 측정 시간에 변화를 일으키는 것을 살펴봅시다.
    # コマンドを実行する際の作業ディレクトリ内のファイルサイズ
    #
    # この情報だけでは、この具体例の結果を再現することはできませんが、大雑把なサイズ感をつかむために記載します。
    $ du -d 0 -h .
    592M  .
    
    # コマンド出力を出力させる場合
    $ time ls -ABFhvoR --color=auto
    ..
    ls --color=auto -ABFhvoR --color=auto  0.11s user 0.08s system 98% cpu 0.196 total
    
    # コマンド出力を捨てる場合
    $ time ls -ABFhvoR --color=auto >/dev/null
    ls --color=auto -ABFhvoR --color=auto > /dev/null  0.05s user 0.07s system 98% cpu 0.125 total
    
    상기 예에서 명령을 버릴지 안 버릴지에 따라 0.08s 정도의 차이가 존재한다.더 많은 출력 명령이 있다면 차이가 더 클 것이다.반대로 출력이 많지 않은 명령이라면 차이가 작아진다.이렇게 하면 측정 명령의 실행 결과를 측정할 때 출력을 버리면 측정 시간이 변경됩니다.따라서 표준 출력과 표준 오류 출력을 교체해야 하며 명령의 출력을 버리지 말아야 한다.
    나는 표준 출력과 표준 오류 출력을 바꿀 수 있는 상황을 찾아냈다.또 다른 유용한 장면이 있다는 것을 아는 사람이 있다면 댓글 등으로 공유할 수 있다.

    좋은 웹페이지 즐겨찾기