명령 행 프로그램이 출력이 재 지정되는 것을 막을 수 있습니까?


49

나는 이것을하는 데 너무 익숙해졌습니다. someprogram >output.file

프로그램이 생성하는 출력을 파일에 저장하려고 할 때마다 수행합니다. 또한이 IO 리디렉션 의 두 가지 변형을 알고 있습니다 .

  • someprogram 2>output.of.stderr.file (stderr 용)
  • someprogram &>output.stderr.and.stdout.file (stdout + stderr 겸용)

오늘 나는 불가능하다고 생각한 상황을 겪었습니다. 다음 명령을 사용하고 xinput test 10예상대로 다음과 같은 결과가 나타납니다.

user @ hostname : ~ $ xinput 테스트 10
키 프레스 30 
키 릴리스 30 
키 프레스 40 
키 릴리스 40 
키 누름 32 
키 릴리스 32 
키 누름 65 
키 릴리스 65 
키 누름 61 
키 릴리스 61 
키 누름 31 
^ C
user @ hostname : ~ $ 

이 출력은 평소처럼을 사용하여 파일에 저장할 수있을 것으로 기대했습니다 xinput test 10 > output.file. 그러나 내 기대와 달리 파일 output.file은 비어 있습니다. 이것은 xinput test 10 &> output.filestdout 또는 stderr에서 무언가를 놓치지 않도록하기 위해서도 마찬가지입니다 .

나는 정말로 혼란 스러우므로 xinput프로그램이 출력을 리디렉션하지 않는 방법이 있는지 물어보십시오 .

최신 정보

나는 출처를 보았다. 이 코드에 의해 출력이 생성되는 것 같습니다 (아래 스 니펫 참조). 출력이 일반 printf에 의해 생성되는 것처럼 보입니다.

// 파일 test.c에서

static void print_events (디스플레이 * dpy)
{
    XEvent 이벤트;

    while (1) {
    XNextEvent (dpy, & Event);

    // [... 다른 이벤트 유형은 여기에서 생략됩니다 ...]

        if ((Event.type == key_press_type) ||
           (Event.type == key_release_type) {
        int 루프;
        XDeviceKeyEvent * key = (XDeviceKeyEvent *) & Event;

        printf ( "key % s % d", (Event.type == key_release_type)? "release": "press", 키-> 키 코드);

        for (loop = 0; loopaxes_count; 루프 ++) {
        printf ( "a [% d] = % d", 키-> first_axis + 루프, 키-> axis_data [loop]);
        }
        printf ( "\ n");
    } 
    }
}

소스를 이것으로 수정했습니다 (아래 스 니펫 참조). stderr에 출력 사본을 가질 수 있습니다. 이 출력은 리디렉션 할 수 있습니다.

 // 파일 test.c에서

static void print_events (디스플레이 * dpy)
{
    XEvent 이벤트;

    while (1) {
    XNextEvent (dpy, & Event);

    // [... 다른 이벤트 유형은 여기에서 생략됩니다 ...]

        if ((Event.type == key_press_type) ||
           (Event.type == key_release_type) {
        int 루프;
        XDeviceKeyEvent * key = (XDeviceKeyEvent *) & Event;

        printf ( "key % s % d", (Event.type == key_release_type)? "release": "press", 키-> 키 코드);
        fprintf (stderr, "key % s % d", (Event.type == key_release_type)? "release": "press", 키-> 키 코드);

        for (loop = 0; loopaxes_count; 루프 ++) {
        printf ( "a [% d] = % d", 키-> first_axis + 루프, 키-> axis_data [loop]);
        }
        printf ( "\ n");
    } 
    }
}

현재 내 생각은 리디렉션을 수행하면 프로그램이 키 누름 키 릴리스 이벤트를 모니터링하는 기능을 잃어 버릴 수 있다는 것입니다.

답변:


55

stdout이 터미널이 아닌 경우 출력이 버퍼링됩니다.

그리고을 누르면 Ctrl-C버퍼가 아직 작성되지 않은 경우 손실됩니다.

를 사용하여 무엇이든 동일한 동작을 얻을 수 stdio있습니다. 예를 들어보십시오.

grep . > file

비어 있지 않은 몇 줄을 입력하고을 누르면 Ctrl-C파일이 비어있는 것을 볼 수 있습니다.

반면에 다음을 입력하십시오.

xinput test 10 > file

버퍼 가 가득 차도록 키보드에서 충분히 입력하십시오 (최소 4k에 해당하는 출력). 파일 크기가 한 번에 4k 단위로 커지는 것을 볼 수 있습니다.

을 사용하면 버퍼를 플러시 한 후 for 를 정상적으로 종료 grep할 수 있습니다 . 에 대해서는 그런 옵션이 없다고 생각합니다.Ctrl-Dgrepxinput

기본적 stderr으로 버퍼링되지 않아서 다른 동작을하는 이유를 설명합니다.fprintf(stderr)

에있는 경우 xinput.c, 당신은 추가 signal(SIGINT, exit), 그 말씀이다 xinput는 수신 정상적으로 때 종료 SIGINT당신이 볼 수없는 것, file무엇을 고려 : 안전이 보장되지는 신호 처리기에서 라이브러리 함수를 호출로 (이 충돌하지 않는 가정이 더 이상 비어 printf가 버퍼에 쓰는 동안 신호가 들어 오면 발생할 수 있습니다).

사용 가능한 경우 stdbuf명령을 사용 하여 stdio버퍼링 동작 을 변경할 수 있습니다 .

stdbuf -oL xinput test 10 > file

있습니다 이 사이트에 많은 질문 하지 않도록 다루 표준 입출력 당신이 더 많은 다른 솔루션을 찾을 수 있습니다 유형 버퍼링.


2
와우 :) 그 트릭을했다. 감사합니다. 결국 문제에 대한 나의 인식은 틀렸다. 재지향 리디렉션에 대한 위치는 없었으며 데이터를 플러시하기 전에 Ctrl-C를 중지했습니다. 감사합니다
humanandANDpeace

stdout의 버퍼링을 막는 방법이 있었습니까?
humanandANDpeace

1
@Stephane Chazelas : 자세한 설명을 주셔서 감사합니다. 이미 말한 것 외에도 버퍼로 버퍼링을 설정할 수 있음을 알았습니다 setvbuf(stdout, (char *) NULL, _IONBF, NULL). 어쩌면 이것도 흥미 롭습니다!?
user1146332

4
@ user1146332, 그렇습니다 . 출력이 터미널로 갈 때와 같이 라인 버퍼링 stdbuf -o0stdbug -oL복원 합니다 . 트릭을 사용하여 stdbuf응용 프로그램을 강제로 호출 setvbuf합니다 LD_PRELOAD.
Stéphane Chazelas

다른 workaroudn : unbuffer test 10 > file( 도구의 unbuffer일부입니다 expect)
Olivier Dulac

23

명령은 /dev/tty정기적 인 리디렉션이 발생하지 않도록 직접 쓸 수 있습니다 .

$ cat demo
#!/bin/ksh
LC_ALL=C TZ=Z date > /dev/tty
$ ./demo >demo.out 2>demo.err
Fri Dec 28 10:31:57  2012
$ ls -l demo*
-rwxr-xr-x 1 jlliagre jlliagre 41 2012-12-28 11:31 demo
-rw-r--r-- 1 jlliagre jlliagre  0 2012-12-28 11:31 demo.err
-rw-r--r-- 1 jlliagre jlliagre  0 2012-12-28 11:31 demo.out

귀하의 예는 포인트 +가 질문에 대답하게합니다. 네 가능합니다. 프로그램이 그렇게하는 것은 물론 "예기치 못한"것이며, ​​바람직하지 않은 일이며, 적어도 그러한 일을 고려하지 않는 것은 저를 속였습니다. user1146332의 답변은 리디렉션을 피하는 확실한 방법 인 것 같습니다. 공평하고 주어진 대답이 명령 줄 프로그램 출력을 파일로 리디렉션하는 것을 피할 수있는 가능한 방법이기 때문에 나는 내가 생각하는 대답을 선택할 수 없습니다. 감사합니다!
humanandANDpeace

1
FTR, /dev/ttyLinux 시스템에서 작성된 출력을 캡처 하려면 script -c ./demo demo.log(from util-linux)을 사용하십시오.
ndim

tty에서 실행하지 않고 pty에서 실행중인 경우 procfs (/ proc / $ PID / fd / 0 등)를 보면 찾을 수 있습니다. 적절한 pty에 쓰려면 상위 프로세스의 fd 디렉토리로 이동하여 / dev / pts / [0-9] +에 대한 심볼릭 링크인지 확인하십시오. 그런 다음 해당 장치에 씁니다 (또는 pts가 아닌 경우 재귀).
dhasenan

9

xinput파일에 대한 출력은 거부하지만 터미널에 대한 출력은 거부하지 않는 것 같습니다 . 이를 위해서는 아마도 xinput시스템 호출을 사용하십시오

int isatty(int fd)

열 파일 설명자가 터미널을 참조하는지 여부를 확인합니다.

나는라는 프로그램으로 얼마 전에 같은 현상을 발견했다 dpic. 소스와 일부 디버깅을 살펴본 후 관련 줄을 제거 isatty하고 모든 것이 다시 예상대로 작동했습니다.

그러나 나는이 경험이 매우 혼란 스럽다는 것에 동의합니다.)


나는 나의 설명이 정말로 있다고 생각했다. 그러나 (1) 소스 (xinput 소스 패키지의 test.c 파일)를 보면 isatty테스트가 수행 되지 않습니다 . 출력은 printf함수 (표준 C라고 생각합니다)에 의해 생성됩니다 . 나는 약간을 추가 fprintf(stderr,"output")했고 이것은 리디렉션 할 수 있고 xinput의 경우 전체 코드가 실제로 실행되었음을 증명합니다. 결국 첫 번째 흔적이었던 제안에 감사드립니다.
humanandANDpeace

0

당신의에서 test.c파일 당신은 사용 버퍼링 된 데이터를 플러시 할 수 (void)fflush(stdout);귀하의 직후 printf문.

    // in test.c
    printf("key %s %d ", (Event.type == key_release_type) ? "release" : "press  ", key->keycode);
    //fprintf(stderr,"key %s %d ", (Event.type == key_release_type) ? "release" : "press  ", key->keycode);
    //(void)fflush(NULL);
    (void)fflush(stdout);

명령 행에서 명령 xinput test 10으로 의사 터미널 (pty)에서 실행하여 라인 버퍼 출력을 사용할 수 있습니다 script.

script -q /dev/null xinput test 10 > file      # FreeBSD, Mac OS X
script -c "xinput test 10" /dev/null > file    # Linux

-1

예. 파스칼로 프로그래밍 할 때 DOS 시간 에도이 작업을 수행했습니다. 나는 원칙이 여전히 유지한다고 생각합니다.

  1. stdout 닫기
  2. 콘솔로 stdout을 다시여십시오
  3. 출력을 stdout에 씁니다.

이로 인해 파이프가 파손되었습니다.


“stdout 다시 열기”: stdout은 파일 설명자 1로 정의됩니다. 파일 설명자 1을 다시 열 수 있지만 어떤 파일을 열겠습니까? 아마도 터미널을 연다는 의미 일 것입니다.이 경우 프로그램이 fd 1에 쓰고 있는지는 중요하지 않습니다.
Gilles 'SO-stop

@Gilles 파일은 내가 기억하는 한 "con :"이었습니다. 그러나 예, 그 방향으로 포인트 2를 수정했습니다.
Nils

con유닉스가 호출하는 것에 대한 DOS 이름 /dev/tty, 즉 (제어) 터미널입니다.
Gilles 'SO- 악마 그만
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.