저는 시작 시 실행되는 일부 스크립트를 작업하고 rc.local
있는데 출력 리디렉션이 매우 이상한 방식으로 작동하는 것을 발견했습니다.
그런 식으로 작성하면 echo "foo" >&1
시스템 로그에 기록되고 모든 것이 정상입니다.
echo "foo" >>/dev/stdout
하지만 , 또는 라고 쓰면 echo "foo" >>/proc/self/fd/1
No such device or address라는 오류가 발생합니다.
추가 조사에 따르면 이는 /proc/self/fd/1
실제로 소켓이었습니다. ls -l /proc/self/fd
인쇄 콘텐츠에는 rc.local
다음이 포함됩니다.
lr-x------. 1 root root 64 Jul 14 05:28 0 -> /dev/null
lrwx------. 1 root root 64 Jul 14 05:28 1 -> socket:[18485]
lrwx------. 1 root root 64 Jul 14 05:28 2 -> socket:[18485]
lr-x------. 1 root root 64 Jul 14 05:28 255 -> /etc/rc.d/rc.local
설명된 동작은 를 사용하여 쉽게 재현할 수 있습니다 nc
. 먼저, 어떤 파일 설명자가 소켓에 바인딩되어 있는지 알아보세요. nc -l -p 25566 -c "ls -l /proc/self/fd"
첫 번째 터미널에서 발행한 다음 telnet localhost 25566
다른 터미널에서 발행 하여 이를 수행할 수 있습니다 . 제 경우에는 다섯 번째 설명자입니다.
좋아요 그런 다음 첫 번째 터미널에서 문제를 재현하려면 다음을 수행하십시오.
nc -l -p 25566 -c "echo 'hello' >&5"
두 번째 터미널에서:
telnet localhost 25566
연결 설정 및 종료를 위한 텔넷 메시지 사이의 두 번째 터미널 출력에 "hello"가 표시됩니다.
이제 일어나는 일은 파일 설명자 의사 파일이 다음에서 온다는 것입니다 /proc
.
첫 번째 터미널:
nc -l -p 25566 -c "echo 'hello' >/proc/self/fd/5"
두 번째 터미널:
telnet localhost 25566
이제 두 번째 터미널에는 연결이 설정되고 즉시 닫히는 것에 대한 텔넷 메시지만 포함되며 첫 번째 터미널에는 오류가 표시됩니다 sh: /proc/self/fd/5: No such device or address
.
편집하다:운영 체제는 Fedora 23 i686 서버입니다.
자, 여기서 문제가 발생합니다. >&1
이들그리고 그리고 의 차이점은 무엇인가요 >/proc/self/fd/1
? 현재 표준 출력과 정확히 일치하는 일부 파일로 출력을 리디렉션하는 일반적이고 안정적인 방법이 있습니까?
감사해요.
편집 2:
명확성을 위해 위 시나리오에서 Fedora 23 i686의 정확한 입력/출력은 다음과 같습니다.
Terminal 1 | Terminal 2
$ nc -l -p 25566 -c 'ls -go /proc/self/fd' |
| $ telnet localhost 25566
| Trying ::1...
| Connected to localhost.
| Escape character is '^]'.
| total 0
| lr-x------ 1 64 Jul 14 08:56 0 -> pipe:[19687]
| l-wx------ 1 64 Jul 14 08:56 1 -> pipe:[19688]
| lrwx------ 1 64 Jul 14 08:56 2 -> /dev/tty2
| lr-x------ 1 64 Jul 14 08:56 3 -> pipe:[19687]
| lr-x------ 1 64 Jul 14 08:56 4 -> /proc/1285/fd
| lrwx------ 1 64 Jul 14 08:56 5 -> socket:[19686]
| l-wx------ 1 64 Jul 14 08:56 7 -> /pipe:[19688]
| Connection closed by foreign host.
$ nc -l -p 25566 -c "echo 'hi' >&5" |
| $ telnet localhost 25566
| Trying ::1...
| Connected to localhost.
| Escape character is '^]'.
| hi
| Connection closed by foreign host.
$ nc -l -p 25566 -c "echo 'hi' >/proc/self/fd/5" |
| $ telnet localhost 25566
| Trying ::1...
| Connected to localhost.
sh: /proc/self/fd/5: No such device or address | Escape character is '^]'.
| Connection closed by foreign host.
$ | $
답변1
사용 >&N
. 이것은 이식 가능하며 보시다시피 실제로 소켓과 함께 작동합니다.
사용하는 유일한 이유 /proc/self/fd
는 파일 이름이 필요한 프로그램을 실행 중이고 이미 열려 있는 파일 설명자를 사용하라는 지시를 받을 수 없는 경우입니다. 예를 들어, <(cmd...)
거의 모든 명령줄 유틸리티가 이름으로 지정된 파일을 열 수 있지만 모든 명령줄 유틸리티가 미리 열린 파일 설명자를 직접 지원하는 것은 아니기 때문에 리디렉션에서는 이를 사용합니다.
그러나 쉘은 기존 파일 설명자를 사용할 수 있으므로 /proc
.
또한 이는 Linux에만 해당되며 내 Linux 시스템에 , 및 다른 항목은 등에 대한 심볼릭 링크 이므로 /proc/NNN/fd/
설치 해야 합니다. 다른 유닉스에서는 다를 수 있습니다. ~에 따르면/proc
/dev/stdout
/dev/fd/*
/proc/self/fd/*
/proc
오래된 질문에 대한 답변 /dev/stdout
POSIX 외부로 명시적으로 나열됩니다.
리디렉션이 원하는 대로 작동하지 않는 이유는 다음과 같습니다. 을 사용해 보십시오. 두 strace
가지의 차이점은 >&N
리디렉션 의 경우 파일 설명자가 bash
호출되며 .txt를 사용하여 일반 파일로 열려고 시도한다는 것입니다. . 이와 같은 새 소켓 복사본을 여는 것은 분명히 지원되지 않으므로 호출이 실패합니다. 스트림 소켓은 거의 지점 간 링크이므로 새 복사본 열기를 비활성화하는 것이 그다지 부자연스러워 보이지는 않습니다. 하지만 왜 파이프나 와 함께 작동하는지 모르겠습니다.dup()
>/proc/self/fd/N
open()
proc
dup
$ ls -l /proc/self/fd/3
lrwx------ 1 itvirta itvirta 64 Jul 14 18:24 /proc/self/fd/3 -> socket:[168157]
$ strace bash -c "echo foo >>/proc/self/fd/3" 2>&1 | grep open.*proc/self
open("/proc/self/fd/3", O_WRONLY|O_CREAT|O_APPEND, 0666) = -1 ENXIO (No such device or address)
반품이 답변이식성에 관한 몇 가지 정보가 있습니다./proc/NNN/fd
답변2
3>&1
기존 리디렉션 반복 과 유사파일 설명자: 이는 동일한 열린 파일(동일한 파일, 동일한 플래그, 동일한 위치 등)을 가져와 프로그램의 다른 "출력 포트"(다른 파일 설명자 번호)에 삽입합니다. (보다 정확하게는 동일한 파일 설명자를 가리키는 새 파일 설명자를 생성합니다.)파일 설명, 그러나 우리는 그렇지 않습니다)
>&1
설명자를 자체에 복사하며 일부 쉘은 이를 완전히 최적화합니다.
>/proc/$pid/fd/1
파일 열기와 같은 리디렉션입니다 /proc/$pid/fd/1
. 그러면 새 파일 설명자가 생성됩니다. 의 파일은 /proc/*/fd
파일을 열 때 기본적으로 기존 파일 설명자의 데이터를 복사한다는 점에서 특별합니다. 이러한 파일은 기호 링크이지만 "마법"입니다. 예를 들어 삭제된 파일이나 파이프는 깨진 기호 링크로 나타나지만 커널에 /proc/*/fd
항목을 처리하는 특수 코드가 포함되어 있기 때문에 여전히 기존 파일처럼 열 수 있습니다. 따라서 대부분의 경우 >&1
및 >/proc/self/fd/1
는 동일합니다. 하지만,소켓은 다르게 취급됩니다..
/*
* In theory you can't get an open on this inode, but /proc provides
* a back door. Remember to keep it shut otherwise you'll let the
* creepy crawlies in.
*/
static int sock_no_open(struct inode *irrelevant, struct file *dontcare)
{
return -ENXIO;
}
(최근 버전에서는 코드가 재구성되었으나, 여전히 소켓을 통과해도 열 수 없습니다 /proc/PID/fd/NUM
.)
대부분의 다른 파일 형식처럼 소켓을 열 수 없는 이유는 소켓에 더 많은 정보가 첨부되어 있고 소켓을 여는 방법을 알려주어야 하기 때문입니다. 예를 들어 TCP 소켓을 열면 소스 포트가 할당됩니다. 어떤 경우에는 이것이 의미가 있을 수 있지만 Linux 커널은 다른 프로세스에 대한 소켓 열기를 허용하지 않습니다. 동일한 프로세스( /proc/self/fd/NUM
대신 ) 의 경우 /proc/OTHERPID/fd/NUM
호출을 open
일반적인 파일 설명자 중복으로 변환할 수 있지만 /proc/self/fd
애초에 여는 것은 특이한 일이며 일반적으로 기능 여유가 없기 때문에 소켓 리디렉션은 수행되지 않습니다. , 따라서 커널은 이 합리적이지만 쓸모없는 예외를 지원하도록 설계되지 않았습니다.