Bash: 다음 파이프의 'no-op'에 의해 '티'가 차단되었습니다.

Bash: 다음 파이프의 'no-op'에 의해 '티'가 차단되었습니다.
  • tee표준 출력이 no-op( ) 명령으로 파이프되면 아무것도 :인쇄되지 않으며 파일 크기는 0입니다.
  • tee표준 출력을 로 파이프하면 cat모든 것이 올바르게 인쇄되고 파일 크기가 0보다 큽니다.

다음은 이를 보여주는 코드 예제입니다(스크립트의 첫 번째 입력 매개변수에 따라 조건부).

#!/usr/bin/env bash

log_filepath="./log.txt"
[ -f "$log_filepath" ] && { rm "$log_filepath" || exit 1 ; }

fail_tee="$1"

while IFS= read -r -d $'\n' line ; do
    printf "%s%s\n" "prefix: " "$line"  | \
    tee -a "$log_filepath"              | \
    {
        if [ -n "$fail_tee" ]; then
            # Nothing is printed to stdout/terminal
            # $log_filepath size is ZERO.
            : # do nothing. 
        else
            # Each line in the input is prefixed w/ "prefix: " and sent to stdout
            # $log_filepath size is 46 bytes
            cat
        fi
    }
done <<'EOF'
1
23
456
7890
EOF

그 뒤에 설명이 있기를 바랍니다.
no-op 명령에 대한 나의 기대 는 출력이 파일로 전송되는 것을 :방해해서는 안 된다는 것입니다 .tee

답변1

:in은 여전히 ​​셸에서 설정한 파이프의 읽기 끝을 유지하는 프로세스 tee ... | :이고 다른 끝은 tee쓰기입니다. 즉시 종료 되므로 :파이프에서 데이터를 읽을 수 없습니다. (파이프라인이 작업을 동시에 수행하려면 쉘은 단지 no-op를 처리하기 위한 것일지라도 파이프라인의 각 부분에 대해 새 프로세스를 생성해야 합니다 :. 귀하의 예에서 프로세스는 if마지막 명령문을 실행합니다. :내장 함수를 "실행"한 후 결국 종료됩니다 ).

일반적인 동작은 파이프 판독기가 종료될 때(읽기측 파일 설명자가 닫힘) 기록기가 다음 쓰기 시 SIGPIPE 신호를 수신하여 종료되도록 하는 것입니다.

이는 파이프라인의 오른쪽이 종료되면 왼쪽도 종료되고 잠재적으로 긴 작업이 쓸데없이 계속되지 않는다는 것을 의미하기 때문에 일반적으로 원하는 것입니다. 또는 (더 나쁜 경우) 데이터가 갈 곳이 없기 때문에 쓰기를 허용하지 않는 차단된 파이프에 무력하게 쓰기를 시도합니다.

tee예외는 없을 것 같으 니까POSIX 사양;가장 가까운 부분에는 파일 피연산자에 대한 쓰기 오류가 언급되어 있습니다.

성공적으로 열린 파일 피연산자에 대한 쓰기가 실패하면 성공적으로 열린 다른 파일 피연산자 및 표준 출력에 대한 쓰기는 계속되어야 하지만 종료 상태는 0이 아니어야 합니다.

SIGPIPE가 무시되면 테스트한 구현이 계속 실행된 EPIPE다음 호출에서 오류를 반환합니다 write().

GNU coreutils 버전에는 쓰기 실패 시 수행할 작업을 제어하는 ​​옵션이 있습니다 tee.-p--output-error

지정되지 않은 경우 기본 작업은 --output-error파이프에 쓰는 동안 오류가 발생하면 즉시 종료하고 비파이프라인 출력에 쓰는 동안 오류를 진단하는 것입니다.

종료 방법은 SIGPIPE를 통한 것이므로 tee신호를 무시한 것부터 시작하면 종료되지 않습니다.

기본 모드 는 종료하도록 하는 다른 옵션과 달리 "파이프라인이 아닌 출력을 쓰는 동안 오류 진단" -p입니다 . warn-nopipe뒤에서는 SIGPIPE 신호도 무시한 다음 파이프에 쓰기 시도를 중지합니다.

따라서 적어도 GNU 버전에서는 tee -p ... | ...이를 사용하여 파이프 판독기가 종료될 때 종료되는 것을 방지할 수 있습니다. 또는 예를 들어 블랙홀을 모방하도록 오른쪽에 프로그램을 배열할 수 있습니다 cat > /dev/null(여전히 표시됨).그리고얻는 모든 것을 기록하지만 커널은 결국 기록된 데이터를 무시합니다 /dev/null.)

답변2

이것은 :nop 프로세스가 아닙니다. 이것은 cat논란의 여지가 없습니다. stdin을 읽지 않고 stdout으로 전달합니다(이것은 nop가 아니기 때문에 nop 파이프라인 단계로 처리될 수 있다는 것을 보았지만 이는 또 다른 아이디어입니다). 이것은 과정이 아닙니다.

당신은 무엇이든 파이프하려고합니다. 케이싱의 용도는 모르겠지만 파이프가 아무 것에도 연결되어 있지 않아도 놀라지 않을 것입니다. (또는 쉘에 연결되어 무시될 수도 있습니다). 어쨌든 좋은 일이 일어나지 않았으면 좋겠어.

@Kusalananda가 말했듯이. 파이프는 출력에서 ​​프로세스가 읽을 때까지 기다리지만(또는 적어도 파이프를 닫을 때) 어떤 프로세스도 파이프를 읽거나 닫을 수 없습니다. cat >/dev/null[(다형성) 으로 파이프할 수 있습니다 . 또는 > /dev/null(정적으로) 리디렉션할 수 있는 경우].

다음 중 하나(가능하면 고양이가 없는 것이 좋음)

| cat >/dev/null
>/dev/null

또 다른 다형성 솔루션(중복된 고양이 부스러기 방지):

if ...
then
  out=/dev/stdout
else
  out=/dev/null
fi

command >"$out"

답변3

:stdin에서 읽지 않으므로 PIPE 신호를 생성합니다. PIPE 신호를 받은 후 tee(기본값)가 종료됩니다. 사용하면 :모든 것이 중지됩니다.

--output-error=warnGNU tee(bash를 사용한 이후 이미 사용했다고 가정)(te POSIX 옵션 아님)를 사용하는 경우 tee에 추가하여 이를 볼 수 있습니다.

다음을 사용하여 이를 테스트할 수 있습니다.

$ echo "hello" | tee --output-error=warn | :
tee: 'standard output': Broken pipe

:로 변경하면 PIPE가 종료 cat >/dev/null되는 것을 방지할 수 있습니다 .tee

하지만:쉘 루프를 사용하여 텍스트를 처리하는 것이 왜 나쁜 습관으로 간주됩니까?

텍스트 파일(여기 문서)을 한 줄씩 읽은 다음 파이프로 보내는 것은 최선의 선택이 아닙니다.

고려하다:

sed 's/^/preffix: /' <<'EOF' | tee -a "$log_filepath"
1
23
456
7890
EOF

변수 값을 기반으로 배열 내에서 티 옵션을 설정할 수도 있습니다.

또한 문서의 여기 대신 변수에 출력을 설정합니다.

#!/bin/bash --

log_filepath="./log.txt"
[ -f "$log_filepath" ] && { rm "$log_filepath" || exit 1 ; }

enable_stdout="${1:+"yes"}"
enable_log="yes" # comment this line to avoid logging.

out=$'1\n23\n456\n7890\n'

unset teeoptions; teeoptions=()
[ -n "$enable_stdout" ] && teeoptions+=(-a "/dev/tty")
[ -n "$enable_log" ]    && teeoptions+=(-a "$log_filepath")

printf '%s' "$out" | 
    sed 's/^/preffix: /' | 
    tee "${teeoptions[@]}" >/dev/null

관련 정보