대화 상자 입력을 변수로 보내는 방법은 무엇입니까?


18

나는 bash 스크립팅을 가르치고 있으며 문제가 발생했습니다. 'read'명령을 사용하여 사용자로부터 입력을 받아 스크립트에서 나중에 사용할 변수를 입력하도록 스크립트를 작성했습니다. 스크립트는 작동하지만 ....

'dialog'를 사용하여 설정을하고 싶습니다. 나는 그것을 알았다

'dialog --inputbox'는 출력을 'stderr'로 보내고 해당 입력을 변수로 가져 오려면 파일로 입력 한 다음 검색해야합니다. 이것을 설명하기 위해 찾은 코드는 다음과 같습니다.

#!/bin/bash
dialog --inputbox \

"What is your username?" 0 0 2> /tmp/inputbox.tmp.$$

retval=$?

input=`cat /tmp/inputbox.tmp.$$`

rm -f /tmp/inputbox.tmp.$$

case $retval in
0)

echo "Your username is '$input'";;
1)

echo "Cancel pressed.";;

esac

2>를 사용하여 sdterr를 /tmp/inputbox.tmp.$$로 보내는 것을 알 수 있지만 출력 파일은 'inputbox.tmp.21661'과 같습니다. 파일을 시도하고 고양이를 치면 오류가 발생합니다. 따라서 여전히 --inputbox에서 사용자 입력을 변수로 가져올 수 없습니다.

스크립트 예 :

echo "  What app would you like to remove? "

read dead_app

sudo apt-get remove --purge $dead_app

보시다시피 기본 스크립트입니다. 변수를 단어로 얻을 수도 dialog --inputbox있습니까?


내 경험상 두 번째 줄 다음에 빈 줄을 제거하면 스크립트가 제대로 작동합니다. 또는 mktemp명령을 사용하여 임시 파일을 작성할 수 있습니다 .
jarno

답변:


16

: DI가 설명 할 수 없습니다 !!! Advanced Bash-Scripting Guide : Chapter 20. I / O Redirection 에서 그들이하는 말을 이해할 수 있다면 , 새로운 답변을 쓰면 50rep 을 줄 것 입니다 .

exec 3>&1;
result=$(dialog --inputbox test 0 0 2>&1 1>&3);
exitcode=$?;
exec 3>&-;
echo $result $exitcode;

참조 : bash의 대화 상자가 변수를 올바르게 잡지 않습니다.

^ @Sneetsher의 답변 (2014 년 7 월 4 일)

요청에 따라이 스 니펫이 한 줄씩 무엇을하는지 설명하려고합니다.

;줄 끝에서 모든 세미콜론을 생략하여 단순화 할 것 입니다. 줄당 하나의 명령을 작성하는 경우에는 필요하지 않기 때문입니다.

I / O-스트림 :

먼저, 통신 스트림을 이해해야합니다. 0에서 9까지 번호가 매겨진 10 개의 스트림이 있습니다.

  • 스트림 0 ( "STDIN") :
    "표준 입력", 키보드에서 데이터를 읽는 기본 입력 스트림.

  • 스트림 1 ( "STDOUT") :
    "표준 출력", 터미널에서 일반 텍스트를 표시하는 데 사용되는 기본 출력 스트림.

  • 스트림 2 ( "STDERR") : "표준 오류", 터미널에서 특수 목적으로 오류 또는 기타 텍스트를 표시하는 데 사용되는 기본 출력 스트림.

  • 스트림 3-9 :
    자유롭게 사용할 수있는 추가 스트림. 기본적으로 사용되지 않으며 무언가를 사용하려고 할 때까지 존재하지 않습니다.

모든 "스트림"은 내부적으로 파일 디스크립터로 표시됩니다 /dev/fd( /proc/self/fd모든 스트림에 대한 다른 심볼릭 링크를 포함하는 심볼릭 링크입니다 ... 조금 복잡하고 동작에 중요하지 않으므로 여기서 멈 춥니 다). 표준 스트림은 또한이 /dev/stdin, /dev/stdout그리고 /dev/stderr(심볼릭 링크는 ... 등, 다시있는).

스크립트 :

  • exec 3>&1

    Bash 내장 기능을 exec사용하여 스트림 리디렉션을 셸에 적용 할 수 있습니다. 이는 다음의 모든 명령에 영향을 미칩니다. 자세한 정보는 help exec터미널에서 실행 하십시오.

    이 특별한 경우에 스트림 3은 스트림 1 (STDOUT)로 경로 재 지정됩니다. 이는 나중에 스트림 3으로 보내는 모든 것이 마치 STDOUT에 정상적으로 인쇄 된 것처럼 터미널에 나타남을 의미합니다.

  • result=$(dialog --inputbox test 0 0 2>&1 1>&3)

    이 라인은 많은 부분과 구문 구조로 구성됩니다.

    • result=$(...)
      이 구조는 대괄호 안에 명령을 실행하고 출력 (STDOUT)을 bash 변수에 지정 result합니다. 를 통해 읽을 수 $result있습니다. 이 모든 것은 veeeery looong에 설명되어 man bash있습니다.

    • dialog --inputbox TEXT HEIGHT WIDTH
      이 명령은 주어진 텍스트, 텍스트 입력 필드 및 두 개의 확인 및 취소 버튼이있는 TUI 상자를 표시합니다. OK를 선택하면 명령이 상태 0으로 종료하고 입력 한 텍스트를 STDERR에 인쇄하고 CANCEL을 선택하면 코드 1로 종료하고 아무 것도 인쇄하지 않습니다. 자세한 내용은를 읽으십시오 man dialog.

    • 2>&1 1>&3
      이들은 두 가지 리디렉션 명령입니다. 오른쪽에서 왼쪽으로 해석됩니다.

      1>&3 명령 스트림 1 (STDOUT)을 사용자 지정 스트림 3으로 리디렉션합니다.

      2>&1 이후에 명령의 스트림 2 (STDERR)를 스트림 1 (STDOUT)로 경로 재 지정합니다.

      즉, 명령이 STDOUT에 인쇄하는 모든 것이 스트림 3에 나타나고 STDERR에 표시하려는 모든 것이 이제 STDOUT으로 리디렉션됩니다.

    따라서 전체 행에 텍스트 프롬프트 (STDOUT에서 스트림 3으로 경로 재 지정됨, 쉘이 다시 STDOUT으로 다시 경로 재 지정됨- exec 3>&1명령 참조 )를 입력하고 입력 된 데이터를 지정합니다 (STDERR을 통해 리턴 된 후 STDOUT으로 경로 재 지정됨) Bash 변수에 result.

  • exitcode=$?

    이 코드 dialog는 예약 된 Bash 변수 $?(항상 마지막 종료 코드를 보유 함)를 통해 이전에 실행 된 명령의 종료 코드 (여기에서 나온 )를 검색하고 이를 자체 Bash 변수에 저장합니다 exitcode. $exitcode다시 읽을 수 있습니다 . 에서 더 많은 정보를 검색 할 수 man bash있지만 시간이 걸릴 수 있습니다 ...

  • exec 3>&-

    Bash 내장 기능을 exec사용하여 스트림 리디렉션을 셸에 적용 할 수 있습니다. 이는 다음의 모든 명령에 영향을 미칩니다. 자세한 정보는 help exec터미널에서 실행 하십시오.

    이 특별한 경우에, 스트림 3은 "stream-"로 재지향되는데, 이는 단지 그것이 폐쇄되어야한다는 것을 의미한다. 스트림 3으로 전송 된 데이터는 이제 더 이상 어디에서나 리디렉션되지 않습니다.

  • echo $result $exitcode

    이 간단한 echo명령 (추가 정보 man echo)은 두 개의 Bash 변수 resultexitcodeSTDOUT 의 내용을 인쇄합니다 . 더 이상 명시 적 또는 암시 적 스트림 리디렉션이 없으므로 STDOUT에 실제로 표시되므로 터미널에 간단히 표시됩니다. 기적입니다! ;-)

요약:

먼저 커스텀 스트림 3으로 보낸 모든 것을 STDOUT으로 다시 리디렉션하도록 쉘을 설정하여 터미널에 표시되도록합니다.

그런 다음 dialog명령 을 실행 하고 원래 STDOUT을 사용자 지정 스트림 3으로 리디렉션합니다. 결국 표시되어야하기 때문에 일시적으로 다른 용도로 STDOUT 스트림을 사용해야합니다.
대화창의 사용자 입력이 반환되는 명령의 원래 STDERR을 나중에 STDOUT으로 리디렉션합니다.
이제 STDOUT (STDERR에서 리디렉션 된 데이터를 보유 함)을 캡처하여 variable에 저장할 수 $result있습니다. 원하는 사용자 입력이 포함되어 있습니다!

또한 dialog명령의 종료 코드가 필요합니다.이 코드는 확인 또는 취소를 클릭했는지 표시합니다. 이 값은 예약 된 Bash 변수에 표시 $?되며 우리는 자신의 변수에 복사합니다 $exitcode.

그런 다음 스트림 3을 다시 닫아 더 이상 필요하지 않으므로 스트림의 추가 리디렉션을 중지합니다.

마지막으로, 변수의 내용 $result(대화창의 사용자 입력)과 $exitcode(0은 OK, 1은 CANCEL)의 내용을 터미널에 출력합니다.


나는 사용 exec이 불필요하게 복잡 하다고 생각 합니다. 왜 우리가 --stdout옵션을 선택 dialog하거나 출력을 리디렉션 하지 2>&1 >/dev/tty않습니까?
jarno

답변을 참조하십시오 .
jarno

3
좋은 답변입니다! 그러나 나는 당신이 틀린 하나의 메모를 가지고 있다고 생각합니다-당신은 "그들은 오른쪽에서 왼쪽으로 해석 될 것입니다"라고 말하지만 나는 그것이 사실이 아니라고 생각합니다. bash는 매뉴얼에서 gnu.org/software/bash/manual/html_node/Redirections.html 그들이가 발생하는대로 리디렉션이 발생하는 것을 나타냅니다 (즉 왼쪽에서 오른쪽으로)
ralfthewise

14

대화 상자 자체 도구 사용 : --output-fd 플래그

대화 상자에 대한 매뉴얼 페이지를 읽는 경우 --output-fd기본적으로 STDERR로 이동하는 대신 출력이 진행되는 위치 (STDOUT 1, STDERR 2)를 명시 적으로 설정할 수있는 옵션 이 있습니다.

아래 dialog에서 출력이 파일 설명자 1로 이동하여 MYVAR에 저장할 수 있음을 명시 적으로 지정하여 sample command 실행 중을 볼 수 있습니다 .

MYVAR=$(dialog --inputbox "THIS OUTPUT GOES TO FD 1" 25 25 --output-fd 1)

여기에 이미지 설명을 입력하십시오

명명 된 파이프 사용

숨겨진 잠재력이 많은 대안은 named pipe라는 것을 사용하는 것 입니다.

#!/bin/bash

mkfifo /tmp/namedPipe1 # this creates named pipe, aka fifo

# to make sure the shell doesn't hang, we run redirection 
# in background, because fifo waits for output to come out    
dialog --inputbox "This is an input box  with named pipe" 40 40 2> /tmp/namedPipe1 & 

# release contents of pipe
OUTPUT="$( cat /tmp/namedPipe1  )" 


echo  "This is the output " $OUTPUT
# clean up
rm /tmp/namedPipe1 

여기에 이미지 설명을 입력하십시오

대체 접근 방식 을 사용하는 user.dz의 답변에 대한 심층적 개요

user.dz의 원래 답변과 ByteCommander의 설명 은 좋은 해결책과 그 기능에 대한 개요를 제공합니다. 그러나 더 깊은 분석이 효과가 있는지 설명하는 데 도움이 될 수 있다고 생각 합니다.

우선, 우리가 해결하려는 문제는 무엇이며 우리가 다루고있는 쉘 메커니즘의 기본 작동은 무엇인지 이해하는 것이 중요합니다. 이 작업은 명령 대체를 통해 명령의 출력을 캡처하는 것입니다. 모든 사람이 알고있는 단순한 개요에서 명령 대체 stdout는 명령의 명령을 캡처하여 다른 것으로 재사용 할 수있게합니다. 이 경우 result=$(...)부품은 지정된 모든 명령의 출력을 ...이라는 변수에 저장해야합니다 result.

후드 아래에서 명령 대체는 실제로 파이프로 구현되며, 하위 프로세스 (실행되는 실제 명령)와 읽기 프로세스 (출력을 변수에 저장)가 있습니다. 이것은 간단한 시스템 호출 추적으로 분명합니다. 파일 설명자 3은 파이프의 읽기 끝이고 4는 쓰기 끝입니다. 파일 디스크립터 1에 echo쓰는 하위 프로세스의 경우 stdout해당 파일 디스크립터는 실제로 파일 디스크립터 4의 사본이며 파이프의 쓰기 엔드입니다. 공지 사항 stderr은 연결 파이프의 간단하기 때문에, 여기에 역할이 재생되지 않는 stdout경우에만이.

$ strace -f -e pipe,dup2,write,read bash -c 'v=$(echo "X")'
...
pipe([3, 4])                            = 0
strace: Process 6200 attached
[pid  6199] read(3,  <unfinished ...>
[pid  6200] dup2(4, 1)                  = 1
[pid  6200] write(1, "X\n", 2 <unfinished ...>
[pid  6199] <... read resumed> "X\n", 128) = 2
[pid  6200] <... write resumed> )       = 2
[pid  6199] read(3, "", 128)            = 0
[pid  6200] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=6200, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++

잠시 원래 답변으로 돌아가 봅시다. 이제 우리 dialog는 TUI 상자에을 쓰고 stdout, 대답하고 stderr, 명령 대체 stdout가 다른 곳으로 파이프 된다는 것을 알고 있으므로 이미 솔루션의 일부를 가지고 stderr있습니다. 독자 프로세스에 파이프 될 방식으로 파일 설명자를 다시 연결해야합니다. 이것이 2>&1답 의 일부입니다. 그러나 TUI box로 무엇을합니까?

파일 디스크립터 3이 들어온 곳입니다. dup2()syscall을 사용하면 파일 디스크립터를 복제하여 동일한 위치를 효과적으로 참조 할 수 있지만 개별적으로 조작 할 수 있습니다. 제어 터미널이 연결된 프로세스의 파일 디스크립터는 실제로 특정 터미널 장치를 가리 킵니다. 당신이 할 경우 이것은 분명하다

$ ls -l /proc/self/fd
total 0
lrwx------ 1 user1 user1 64 Aug 20 10:30 0 -> /dev/pts/5
lrwx------ 1 user1 user1 64 Aug 20 10:30 1 -> /dev/pts/5
lrwx------ 1 user1 user1 64 Aug 20 10:30 2 -> /dev/pts/5
lr-x------ 1 user1 user1 64 Aug 20 10:30 3 -> /proc/6424/fd

/dev/pts/5내 현재 의사 터미널 장치는 어디에 있습니까 ? 따라서이 대상을 어떻게 든 저장할 수 있으면 터미널 화면에 TUI 상자를 계속 쓸 수 있습니다. 그게 뭐야 exec 3>&1. command > /dev/null예를 들어 리디렉션 을 사용 하여 명령을 호출 하면 셸은 stdout 파일 설명자를 전달한 다음 dup2()해당 파일 설명자를에 쓰는 데 사용 합니다 /dev/null. 이 exec명령은 전체 셸 세션에 대해 파일 설명자 와 유사한 기능을dup2() 수행 하므로 명령이 이미 리디렉션 된 파일 설명자를 상속합니다. 와 동일합니다 exec 3>&1. 파일 디스크립터 3는 이제 제어 터미널을 참조 / 지시하며, 해당 쉘 세션에서 실행되는 모든 명령이 이에 대해 알 것입니다.

따라서 result=$(dialog --inputbox test 0 0 2>&1 1>&3);쉘은 대화 상자를 작성할 파이프를 작성하지만 2>&1먼저 명령의 파일 디스크립터 2를 해당 파이프의 쓰기 파일 디스크립터에 복제합니다 (따라서 출력이 파이프의 끝과 변수로 읽히도록합니다) 파일 디스크립터 1이 3에 복제됩니다. 그러면 파일 디스크립터 1이 여전히 제어 터미널을 참조하게되고 TUI 대화 상자가 화면에 표시됩니다.

현재 프로세스의 전류 제어 터미널에 대한 약칭이 /dev/tty있습니다. 따라서 파일 디스크립터를 사용하지 않고 솔루션을 간단히 다음과 같이 단순화 할 수 있습니다.

result=$(dialog --inputbox test 0 0 2>&1 1>/dev/tty);
echo "$result"

기억해야 할 주요 사항 :

  • 파일 디스크립터는 각 명령에 의해 쉘에서 상속됩니다.
  • 명령 대체는 파이프로 구현됩니다.
  • 복제 된 파일 디스크립터는 원래 파일 디스크립터와 동일한 위치를 참조하지만 각 파일 디스크립터를 개별적으로 조작 할 수 있습니다

또한보십시오


맨 페이지에는 --stdout옵션이 위험 할 수 있으며 일부 시스템에서 쉽게 실패 할 수 있다고 나와 --output-fd 1있으며 동일한 작업을 수행 한다고 생각 합니다 --stdout: Direct output to the standard output. This option is provided for compatibility with Xdialog, however using it in portable scripts is not recommended, since curses normally writes its screen updates to the standard output. If you use this option, dialog attempts to reopen the terminal so it can write to the display. Depending on the platform and your environment, that may fail..-그러나 명명 된 파이프 아이디어는 훌륭합니다!
바이트 사령관

@ByteCommander "실패 할 수 있습니다"는 예제를 제공하지 않기 때문에 매우 설득력이 없습니다. 또한에 대해서는 언급하지 않았으며 --output-fd여기서 사용하지 않은 옵션 --stdout입니다. 둘째, 대화 상자가 stdout에 먼저 그려지고 반환되는 출력이 두 번째입니다. 우리는이 두 가지를 동시에하지 않습니다. 그러나 --output-fd 특별히 fd 1 (STDOUT)을 사용할 필요는 없습니다. 그것은 다른 파일 디스크립터로 쉽게 재 지정 될 수 있습니다
Sergiy Kolodyazhnyy

확실하지 않습니다. 어쩌면 모든 곳에서 작동하거나 대부분의 시스템에서만 작동 할 수 있습니다. 그것은 내 작업에서 작동하며 맨 페이지는 비슷한 옵션을 조심스럽게 사용한다고 말합니다. 그러나 이미 말했듯이 어쨌든 명명 된 파이프에는 +1이 필요합니다.
바이트 사령관

균형을 유지하기 위해 여기에 의견을 말해야합니다. 나에게 이것은 유일한 정식 답변 일 수 있습니다 (1) 동일한 도구 만 사용하고 외부 도구없이 옵션을 구현했습니다 (2) 우분투에서 작동하며 AU에 관한 모든 것. : / 슬프게도 OP는이 질문을 버린 것 같습니다.
user.dz

일반 파일 대신 명명 된 파이프를 사용하면 어떤 이점이 있습니까? 사용 후 파이프를 삭제하지 않겠습니까?
jarno

7

: DI가 설명 할 수 없습니다 !!! 그들이 참조 : Advanced Bash-Scripting Guide : Chapter 20. I / O Redirection 에서 말하고있는 것을 이해할 수 있다면 , 새로운 답변을 쓰면 50rep 을 줄 것 입니다

현상금이 주어졌습니다. 설명은 ByteCommander의 답변을 참조하십시오 . :) 이것은 역사의 일부입니다.

exec 3>&1;
result=$(dialog --inputbox test 0 0 2>&1 1>&3);
exitcode=$?;
exec 3>&-;
echo $result $exitcode;

소스 : bash의 대화 상자가 변수를 올바르게 가져 오지 못합니다.
참조 : Advanced Bash-Scripting Guide : Chapter 20. I / O Redirection


그 제안은 여전히 ​​유효합니까? 나는 당신이 1 년 반 전에 발견 한 것을 설명 할 수 있다고 생각합니다 ... :-)
바이트 사령관

@ByteCommander, 그러나 당신이 그것을 제공 할 수 있다면, 나는 당신에게 그것을 줄 것입니다, 나는 내 말에있을 것입니다 : D.
user.dz

@ByteCommander, 게시 후 핑 (ping)하십시오.
user.dz

1
끝마친! askubuntu.com/a/704616/367990 모든 것을 이해하고 "유레카"를 즐기시기 바랍니다. 순간. :-D 불분명 한 것이 있으면 의견을 남겨주십시오.
바이트 사령관

4

이것은 나를 위해 작동합니다 :

#!/bin/bash
input=$(dialog --stdout --inputbox "What is your username?" 0 0)
retval=$?

case $retval in
${DIALOG_OK-0}) echo "Your username is '$input'.";;
${DIALOG_CANCEL-1}) echo "Cancel pressed.";;
${DIALOG_ESC-255}) echo "Esc pressed.";;
${DIALOG_ERROR-255}) echo "Dialog error";;
*) echo "Unknown error $retval"
esac

매뉴얼 페이지에서 dialog--stdout에 대해 알려줍니다.

표준 출력으로 직접 출력. 이 옵션은 Xdialog와의 호환성을 위해 제공되지만, curses는 일반적으로 화면 업데이트를 표준 출력에 작성하므로 이식 가능한 스크립트에서 사용하지 않는 것이 좋습니다. 이 옵션을 사용하면 대화 상자가 터미널을 다시 열어서 디스플레이에 쓸 수 있도록합니다. 플랫폼과 환경에 따라 실패 할 수 있습니다.

어느 플랫폼이나 환경에서 작동하지 않는지 누구나 알 수 있습니까? 대신 dialog출력을 리디렉션 하면 2>&1 >/dev/tty더 잘 작동합니까?


4

다른 사람이 Google에서 너무 착륙 했으며이 질문에 특별히 bash가 필요하지만 다른 대안이 있습니다.

zenity 를 사용할 수 있습니다 . Zenity는 bash 스크립트 내에서 사용할 있는 그래픽 유틸리티입니다 . 그러나 물론 이것은 user877329가 올바르게 지적한대로 X 서버가 필요합니다.

sudo apt-get install zenity

그런 다음 스크립트에서 :

RETVAL=`zenity --entry --title="Hi" --text="What is your username"`

유용한 링크 .


3
X 서버가없는 경우
user877329

1
OP는에 대해 알고 싶어합니다 dialog. 마치 와서 "파이썬으로 어떻게 작성하고 쓰나요?"라고 물어 보는 것 같은데, 당신은 저에게 bash를주었습니다. 저는 이것이 다른 방식으로 수행 될 수있어서 매우 기쁘지만, 제가 묻는 것은 아닙니다
Sergiy Kolodyazhnyy

@Serg 귀하의 의견이 유효하지 않습니다. 제 대답은 아닙니다 : 유틸리티는 OP가 요청한 솔루션에 대한 완벽하고 타당한 대안을 제공합니다.
Wtower

3

Sneetsher가 제공하는 대답은 다소 우아하지만 잘못된 점을 설명 할 수 있습니다. 값은 백쉘 $$내부에서 다릅니다 (새 쉘을 시작 $$하고 현재 쉘의 PID 이기 때문에 ). 파일 이름을 변수에 넣은 다음 대신 해당 변수를 참조하십시오.

#!/bin/bash
t=$(mktemp -t inputbox.XXXXXXXXX) || exit
trap 'rm -f "$t"' EXIT         # remove temp file when done
trap 'exit 127' HUP STOP TERM  # remove if interrupted, too
dialog --inputbox \
    "What is your username?" 0 0 2>"$t"
retval=$?
input=$(cat "$t")  # Prefer $(...) over `...`
case $retval in
  0)    echo "Your username is '$input'";;
  1)    echo "Cancel pressed.";;
esac

이 경우 임시 파일을 피하는 것이 더 나은 솔루션이지만 임시 파일을 피할 수없는 많은 상황이 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.