Linux 쉘 명령 붙여넣기 문제

Linux 쉘 명령 붙여넣기 문제

나는 종종 스크립트를 실행하고 여기에 STDIN 몇 줄을 입력해야 합니다. 이 작업을 수행할 때 일반적으로 통화를 준비한 다음 한 터미널 창에서 다른 터미널 창으로 복사하여 붙여넣습니다. 이상적으로는 다음과 같은 텍스트 블록을 붙여넣을 수 있기를 바랍니다.

<script name> <script args>
<STDIN line 1>
<STDIN line 2>
...

그런 다음 Ctrl-d를 눌러 STDIN이 입력 끝에 도달했음을 스크립트에 표시합니다.

bash에서 (단일 붙여넣기로) 이 작업을 수행하면 세션에서 STDIN 에코 없이 스크립트가 실행되고 <cr>^d가 처리되기 전에 추가 항목이 적중되어야 하며 스크립트 STDIN의 첫 번째 줄이 손실됩니다. 그래서 보통 스크립트 호출을 자체 붙여넣기로 붙여넣은 다음 STDIN의 모든 줄을 두 번째 붙여넣기로 붙여넣습니다. 이렇게 하면 줄을 붙여넣고 에코할 수 있으며 Ctrl-d를 눌러 입력 끝을 나타낼 수 있습니다.

zsh에서 이 작업을 수행하면 대부분의 STDIN 줄이 에코되지 않습니다(STDIN 줄이 많으면 마지막 줄 중 일부가 에코되며 첫 번째 줄은 줄의 후행 조각만 에코할 수 있음). , <cr>^d 이전에 추가 필요를 식별한 후.

문제의 스크립트가 Python인지 Perl인지는 중요하지 않습니다. 둘 다 동일하게 작동하므로 이것이 쉘 문제라고 믿게 됩니다.

문제는 문제가 어디에 있으며 각 실행을 단일 붙여넣기로 수행할 수 있는 솔루션이 있습니까?입니다.

실행되는 스크립트의 내용은 실제로 중요하지 않습니다. 내 테스트에서는 while 루프만큼 간단한 스크립트를 사용하여 STDIN을 한 줄씩 버퍼로 읽은 다음 while 루프 외부에 버퍼를 인쇄했습니다. 내가 말하는 것은 <cr>키보드의 키에 따라 Enter 또는 Return 키입니다.

답변1

heredoc을 사용해 보세요. 잘라내고 붙여넣기가 쉽습니다.

script.sh arg arg arg <<'END_INPUT'
line1
line2
line3
END_INPUT

답변2

아마도 관찰 내용은 다음에 따라 달라집니다.라인 편집그리고 쉘은 터미널의 모드를 설정합니다.

텍스트를 붙여넣을 때

cat
a
b

("b" 뒤의 개행 포함)을 대화형 Bash 세션의 명령줄에 추가하면 기본적으로 다음에서 설정을 찾을 수 있습니다.오리지널 모드— 행을 편집할 때와 같습니다.GNU 읽기 라인라이브러리는 무엇보다도 입력이 한 줄씩이 아닌 문자별로 쉘에 전송되어 캐리지 리턴( , 를 \r누를 때 터미널이 보는 내용 Enter또는 명령에 붙여넣을 때 줄바꿈을 변환함을 의미합니다. 행) 개행( \n)은 비활성화되고 입력된 문자는 터미널이 아닌 쉘 자체에 의해 명령줄에 에코됩니다.
그런 다음 셸은 첫 번째 줄을 읽고 그것이 완전한 간단한 명령( cat)임을 확인하고 실행합니다. 이 작업을 수행하는 동안 자체 라인 편집도 비활성화되고 터미널을 "cooked" 모드로 설정하고 \r\n변환을 활성화합니다. 그러나 이 시점에서 입력 버퍼에는 이미 화면에서 a\rb\r읽고 인쇄된 내용이 포함되어 있습니다. 그런 다음 + ( )를 cat누르면 종료되고 새 프롬프트가 인쇄되며 (유일한) 인쇄된 줄을 덮어씁니다(캐리지 리턴으로 끝나기 때문입니다).CtrlD^Dcat

알아채다:

  • 대신 cat루프에서 표준 입력을 읽는 프로그램을 호출하면 스크립트와 동일합니다.

    while IFS= read -r foo
    do
      # Accumulate the content of foo
    done
    

    Enter입력 버퍼에 문자가 없기 때문에 클릭할 때까지 아무 것도 읽지 않습니다 \n(붙여넣은 데이터가 터미널의 입력 버퍼 크기를 초과하지 않는 한, 아래 참조).

  • b붙여넣은 후 명령 뒤에 표시되는 텍스트(이 경우)는 붙여넣은 내용의 에코가 아니라 cat. 예를 들어 붙여넣기를 통해 이를 쉽게 확인할 수 있습니다.

    od -An -tc
    a
    b
    

    쉘 실행에 붙여넣는 것도 strace -f -e read,write bash무슨 일이 일어나고 있는지 이해하는 데 도움이 될 수 있습니다.

  • 표준 입력에서 읽을 붙여넣은 텍스트가 "충분"한 경우(내 시스템에서는 4095자 이상, "정규 및 비정규 모드" 참조)man 3 termios), 위에 작성된 내용은 처음 4095바이트 크기 읽기에만 적용됩니다. 이는 터미널이 원시 모드에서 입력 버퍼에 넣는 최대량입니다. 이후 읽기는 베이킹 모드에서 발생하고 \r\n변환이 발생하며 붙여넣은 텍스트의 해당 부분이 명령줄에 반영됩니다(명령의 출력과 혼합될 수 있음).

원칙적으로 Bash의 줄 편집을 비활성화하면 이 동작을 변경할 수 있습니다.

$ set +o emacs
$ set +o vi
$ cat         # Pasting the above snippet here
a
b
a
b
$             # ^D makes cat exit and brings the prompt back

그러나 "대괄호 붙여넣기"가 활성화되지 않는 한(아래 참조) 처리를 시작하기 전에 붙여넣기 작업이 끝날 때까지 기다리도록 터미널에서 쉘에 지시할 방법이 없기 때문에 긴 입력은 여전히 ​​출력과 혼합됩니다.


zsh조금 더 간단한 것 같습니다. 유일한 문제:"괄호 안에 붙여넣기"기본적으로 활성화될 수 있습니다. 이를 통해 여러 줄 명령을 명령줄에 붙여넣고 특수 문자(예: 탭, 줄 바꿈)가 올바르게 처리되는지 확인하면서 스크립트처럼 실행할 수 있습니다. 이는 또한 주어졌을 때

cat
a
b

zsh세 줄을 읽고 실행하며 cat종료 시 터미널에서 새 입력을 기다리고 cat쉘이 실행을 시도한 a다음(해당 명령이 시스템에 존재하지 않으면 실패함) b.

대괄호 붙여넣기를 비활성화하면 원하는 동작이 달성됩니다(그러나 bash줄 없는 편집과 달리 zsh붙여넣은 내용은 처음 4095바이트를 초과하는 부분을 제외하고 기본적으로 에코되지 않습니다(목록 위의 글머리 기호 참조)).

% unset zle_bracketed_paste
% cat         # Pasting the above snippet here
a             # cat prints these two lines
b
%             # ^D makes cat exit and brings the prompt back

관련 정보