디렉토리의 find
모든 pdf 파일을 볼 때 사용 하고 있습니다 . 그들을 제거하기 위해 시도했습니다./home
access denied
find /home -iname "*.pdf" | grep -v "access denied"
그러나 결과는 동일합니다. 이 줄을 어떻게 제거 할 수 있습니까?
디렉토리의 find
모든 pdf 파일을 볼 때 사용 하고 있습니다 . 그들을 제거하기 위해 시도했습니다./home
access denied
find /home -iname "*.pdf" | grep -v "access denied"
그러나 결과는 동일합니다. 이 줄을 어떻게 제거 할 수 있습니까?
답변:
access denied
출력이 오류이며에 파이프되는 STDOUT 대신 STDERR로 전송되어 시도했기 때문에 작동하지 않았습니다 grep
.
STDERR 만 리디렉션하여 이러한 오류가 표시되지 않도록 할 수 있습니다
find /home -iname "*.pdf" 2>/dev/null
또는 David Foerster가 언급했듯이 STDERR을 더 간결하게 닫을 수 있습니다.
find /home -iname "*.pdf" 2>&-
그러나 실제로 다른 사용자보다는 집을 검색하고 싶기 때문에 실제로 원하는 경우가 있습니다.
find ~ -iname "*.pdf"
오류가 발생하면 로컬 구성에 잘못된 소유권이있을 수 있으므로 조사해야합니다.
sudo chown $USER: ~/.gvfs ~/.dbus
2>&-
. 기능 장애 파일 디스크립터에 오류 메시지를 쓰려고하면 GNU find가 자체 종료되지 않습니다. 소유권 문제 sudo chown -R $USER: ...
는에서 소유하지 않은 파일이 더 많은 경우 더 효과적입니다 $USER
.
액세스 거부가 stderr
아닌 에 인쇄 될 수 stdout
있습니다.
이 시도:
find /home -iname "*.pdf" 2>&1 | grep -v "access denied"
는 2>&1
의 출력 리디렉션 stderr
에를 stdout
그 때문에, grep -v
그 일을 할 수 있습니다. (기본적으로 |
파이프 만 stdout
있고 stderr
)
2>&1
... 나는 배쉬 전문가가 아니므로 잘못된 경우 다음과 같이 말하십시오 :)
bash
제외하고 우분투에는 있습니다 . 나는 그것이 최선의 해결책 이라고 생각합니다 -악의적으로 명명 된 파일 access denied
이 여전히 나타납니다.
당신은 아마 평균 무엇인가 - 어떤 "사용 권한이 거부되었습니다" find
우분투 쇼에서 당신이 때문에 파일 권한-보다는 아니 액세스 뭔가 "액세스 거부"할 수 있습니다.
이 작업을 올바르게 수행하는 완전히 일반적인 명령 하나 (그리고 보너스 메시지는 오류 메시지가 동일한 한 다른 * nix es로 이식 가능 )는 다음과 같습니다.
(find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
(보통 당신은 몇 가지 인수를 전달하려고합니다 find
. 첫 번째 리디렉션 전에갑니다 3>&1
.)
그러나 종종 더 간단한 것을 사용할 수 있습니다. 예를 들어 프로세스 대체를 사용할 수 있습니다 . 자세한 내용은 다음과 같습니다.
두 개의 일반적인 접근 방식은 버릴 수 있습니다 표준 오류 (같이 ZANNA의 대답 ) 나에 대한 표준 오류 리디렉션 표준 출력 (같이 및 필터 표준 출력을 안드로이드 데브의 대답 ). 그것들은 작성하기 쉽다는 장점이 있으며 종종 합리적인 선택이지만, 이러한 접근법은 이상적이지 않습니다.
stderr로 전송 된 모든 항목을 폐기 하면 (예 : 널 장치 로 리디렉션 2>/dev/null
하거나 닫는 2>&-
등) "Permission denied"이외의 오류가 발생할 위험이 있습니다.
"권한이 거부되었습니다"는 실행할 때 발생하는 가장 일반적인 오류 일 수 find
있지만 가능한 유일한 오류는 아니며 다른 오류가 발생하면 이에 대해 알고 싶을 수 있습니다. 특히 find
시작점이 없으면 "해당 파일 또는 디렉토리가 없습니다"라고보고합니다. 시작점이 여러 개인 find
경우에도 여전히 유용한 결과가 반환되어 작동하는 것처럼 보일 수 있습니다. 예를 들어, a
및 c
존재하지만 존재 b
하지 않는 경우에 find a b c -name x
결과를 인쇄 a
한 다음에 "No such file or directory"를 표시 b
한 다음 결과를 표시 c
합니다.
표준 출력으로 함께 표준 출력과 표준 에러를 결합하고 배관 으로 grep
또는 다른 명령과하면으로하는 필터 2>&1 | grep ...
또는 |& grep ...
실수로 이름이 여과되는 메시지를 포함하는 파일을 필터링의 위험 -runs.
예를 들어 "Permission denied"가 포함 된 행을 필터링하면 "Permission denied messages.txt"와 같은 파일 이름을 표시하는 검색 결과도 삭제됩니다. 검색을 방해 할 수 있도록 특수하게 조작 된 이름을 파일에 부여하는 것도 가능하지만 우연히 발생할 수 있습니다.
결합 된 스트림을 여과하는 것은 또 다른 문제점이 있는데, 이는grep -vx 'find: .*: Permission denied'
파이프의 우측에서 와 같이 보다 선택적으로 여과함으로써 완화 될 수 없다 . find
조치 -print
를 지정하지 않을 때 내재 된 조치를 포함하여 일부 조치 는 stdout 이 터미널 인지 여부에 따라 파일 이름을 출력하는 방법을 결정합니다 .
?
대신 인쇄됩니다.grep
하면 find
더 이상 터미널이 표시되지 않습니다. (더 정확하게 말하면, stdout이 터미널이되지 않게됩니다.) 그러면 이상한 문자가 문자 그대로 출력됩니다. 그러나 파이프의 오른쪽에있는 모든 명령이 (a) "Permission denied"메시지처럼 보이는 행을 제거하고 (b) 남아있는 것을 인쇄하는 경우 여전히 find
터미널 인 shenanigans에 종속됩니다. 감지는 방지하기위한 것입니다.man find
파일 이름을 인쇄하는 각 동작의 동작을 포함하여 자세한 내용은 비정상적인 파일 이름 섹션을 참조하십시오 . ( "많은 find 동작은 다른 사용자의 통제하에있는 데이터를 인쇄 할 때 발생합니다 ..." ) GNU Findutils 참조 매뉴얼 의 3.3.2.1 , 3.3.2.2 및 3.3.2.3 절도 참조하십시오 .비정상적인 파일 이름에 대한 위의 논의는 GNU find 와 관련 이 있으며, 이는 find
Ubuntu를 포함한 GNU / Linux 시스템에서 구현됩니다.
당신이 정말로 원하는 것은 stderr 를 배관하는 동안 stdout을 그대로 두는 것 입니다. 불행히도 이에 대한 간단한 구문은 없습니다. 파이프 stdout 및을 포함하여 일부 쉘 (을 포함하여 )이 두 스트림을 모두 파이프 하도록 지원 하거나 stderr를 stdout으로 먼저 리디렉션 할 수 있습니다 . 그러나 일반적으로 사용되는 쉘은 파이프 stderr에만 구문을 제공하지 않습니다.grep
|
bash
|&
2>&1 |
여전히 할 수 있습니다. 어색하다. 한 가지 방법은 stdout을 stderr로 바꾸어 검색 결과가 stderr에 있고 오류가 stdout에있는 경우 stdout을 파이프 grep
하여 필터링합니다.
find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
일반적 find
으로 시작점 (일반적으로 디렉토리 인 검색 위치) 및 술어 (테스트 및 조치)와 같은 인수를에 전달 합니다. 이들은 args
위 대신 사용 됩니다.
이것은 교환하려는 두 표준 스트림 중 하나를 유지하기 위해 새 파일 디스크립터 를 도입 하고,이를 재 지정하기 위해 경로 재 지정을 수행하고, 새 파일 디스크립터를 닫아 작동합니다.
3>&1
stdout (파일 설명자 1)이 나중에 리디렉션 될 때 원래 stdout을 쉽게 쓸 수 있도록 파일 설명자 3을 stdout으로 리디렉션합니다.1>&2
stdout을 stderr로 리디렉션합니다. 파일 디스크립터 3은 여전히 원래 stdout이므로 여전히 액세스 할 수 있습니다.2>&3
stderr을 원래 stdout 인 파일 디스크립터 3으로 경로 재 지정합니다.3>&-
더 이상 필요하지 않은 파일 설명자 3을 닫습니다.그러나이 방법은 검색 결과가 stderr로 전송되고 오류가 stdout으로 전송 되는 단점이 있습니다. 대화식 쉘에서 직접이 명령을 실행하고 더 이상 출력을 파이핑하거나 리디렉션하지 않으면 실제로 중요하지 않습니다. 그렇지 않으면 문제가 될 수 있습니다. 해당 명령을 스크립트에 넣은 다음 누군가 (아마 나중에 나중에) 출력을 재 지정하거나 파이프 하면 예상대로 작동 하지 않습니다 .
해결책은 출력 필터링이 끝나면 스트림을 다시 바꾸는 것 입니다. 파이프 라인의 오른쪽에 위에 표시된 동일한 리디렉션을 적용하면 |
파이프 stdout 만 가능하므로 파이프 라인의 측면은 원래 스트림이 교환되지 않았기 때문에 원래 stderr로 전송 된 출력 만 수신하기 때문에이를 달성하지 못합니다 표준 출력. 대신 서브 쉘 ( related ) 에서(
)
위의 명령을 실행 한 다음 스왑 핑 리디렉션을 적용 할 수 있습니다.
(find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
이 작업을 수행하는 것은 특히 서브 쉘이 아닌 그룹화입니다. 원하는 경우 다음을 사용할 수 있습니다 {
;}
.
{ find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'; } 3>&1 1>&2 2>&3 3>&-
Ubuntu와 같은 GNU / Linux 시스템을 포함하여이를 지원할 수있는 시스템의 Bash를 포함한 일부 셸 을 사용하면 명령을 실행하고 해당 스트림 중 하나에서 리디렉션 할 수있는 프로세스 대체 를 수행 할 수 있습니다. 당신은 리디렉션 할 수 있습니다 find
A와 명령의 표준 오류 grep
를 필터링 명령, 그 리디렉션 grep
표준 오류에 명령의 표준 출력을.
find args 2> >(grep -Fv 'Permission denied' >&2)
크레딧은 이 아이디어 를 위해 Android Dev 로갑니다 .
1>&2
합니다. I가 사용하고 >&2
, 등가이다 ( 관련 ). 원하는 것을 사용하십시오.프로세스 대체를 bash
지원 하지만 sh
Ubuntu 는 dash
그렇지 않습니다. stdout과 stderr를 바꾸는 방법은 여전히 작동하지만이 방법을 사용하려고하면 "구문 오류 : 리디렉션이 예기치 않게 나타납니다." 또한 POSIX 모드bash
에서 실행 하면 프로세스 대체에 대한 지원이 해제됩니다.
bash
POSIX 모드 에서 실행되는 한 가지 상황 은 sh
1 로 호출 될 때 입니다. 따라서 Fedora와 같은 OS를 bash
제공 /bin/sh
하거나 Ubuntu 에서 /bin/sh
심볼릭 링크 포인트를 설정 한 경우 POSIX 모드를 해제하는 사전 명령이 없으면 스크립트 bash
에서 프로세스 대체가 여전히 작동하지 않습니다 sh
. 이 방법을 스크립트에 사용하려면 가장 좋은 방법은 아직 아닌 경우 #!/bin/bash
맨 대신 맨 위에 두는 것입니다 #!/bin/sh
.
1 :이 상황에서는 시작 스크립트에서 명령을 실행 한 후bash
POSIX 모드를 자동으로 켭니다 .
이러한 명령을 테스트 할 수 있으면 유용합니다. 이를 위해 tmp
현재 디렉토리 의 서브 디렉토리를 작성하고 일부 파일 및 디렉토리로 채우고 "권한 거부"오류를 유발하는 권한 중 하나를 제거합니다 find
.
mkdir tmp; cd tmp; mkdir a b c; touch w a/x 'a/Permission denied messages.txt' b/y c/z; chmod 0 b
액세스 가능한 디렉토리 중 하나에 이름에 "Permission denied"가있는 파일 이 있습니다. 경로 find
재 지정 또는 파이프없이 실행 하면이 파일이 표시되지만 액세스 할 수 없는 다른 디렉토리에 대한 실제 "Permission denied"오류도 표시됩니다 .
ek@Io:~/tmp$ find
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: ‘./b’: Permission denied
stdout과 stderr을 모두 파이프 grep
하고 "Permission denied"가 포함 된 행을 필터링하면 오류 메시지가 사라지지만 해당 문구가있는 파일의 검색 결과는 이름에서 숨겨집니다.
ek@Io:~/tmp$ find |& grep -Fv 'Permission denied'
.
./a
./a/x
./c
./c/z
./w
./b
find 2>&1 | grep -Fv 'Permission denied'
동일하고 동일한 출력을 생성합니다.
검색 결과가 아닌 오류 메시지에서만 "권한 거부"를 필터링하는 방법은 성공적입니다. 예를 들어, stdout과 stderr이 교체되는 방법은 다음과 같습니다.
ek@Io:~/tmp$ (find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find args 2> >(grep -Fv 'Permission denied' >&2)
동일한 출력을 생성합니다.
텍스트 "Permission denied"가 포함 되지 않은 stderr로 전송 된 행 이 계속 허용 되도록 다른 오류 메시지를 트리거 할 수 있습니다 . 예를 들어, 여기서는 find
현재 디렉토리 ( .
)를 하나의 시작점으로 사용했지만 존재하지 않는 디렉토리 foo
를 다른 곳으로 실행했습니다.
ek@Io:~/tmp$ (find . foo 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: ‘foo’: No such file or directory
find
의 표준 출력은 아직도 터미널입니다또한 개행과 같은 특수 문자가 문자 적으로 표시되는 명령을 확인할 수 있습니다. (위의 데모와 별도로 수행 할 수 있으며 tmp
디렉토리 에있을 필요는 없습니다 .)
이름에 개행을 가진 파일을 만드십시오 :
touch $'abc\ndef'
일반적으로 디렉토리를 시작점으로 사용 find
하지만 파일도 작동합니다.
$ find abc*
abc?def
stdout을 다른 명령으로 파이프하면 개행이 문자 그대로 출력되어 두 개의 개별 검색 결과 abc
와 의 잘못된 인상을 만듭니다 def
. 우리는 그것을 테스트 할 수 있습니다 cat
:
$ find abc* | cat
abc
def
stderr 만 리디렉션하면이 문제가 발생하지 않습니다.
$ find abc* 2>/dev/null
abc?def
닫는 것도 아닙니다.
$ find abc* 2>&-
abc?def
파이핑 하면 문제 grep
가 발생합니다.
$ find abc* |& grep -Fv 'Permission denied'
abc
def
(로 교체 |&
하는 2>&1 |
것은 동일하며 동일한 출력을 생성합니다.)
표준 출력과 표준 에러를 교환 표준 출력을 파이프 수행 하지 problem- 원인 find
의 표준 출력이됩니다 열려진,하게 하지 파이프를 :
$ find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
abc?def
해당 명령을 그룹화하고 스트림을 다시 교환해도 문제가 발생하지 않습니다.
$ (find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
abc?def
( {
;}
버전은 동일한 출력을 생성합니다.)
프로세스 대체를 사용하여 stderr을 필터링해도 문제가 발생하지 않습니다.
$ find abc* 2> >(grep -Fv 'Permission denied' >&2)
abc?def