'액세스 거부'라인 꺼내기


9

디렉토리의 find모든 pdf 파일을 볼 때 사용 하고 있습니다 . 그들을 제거하기 위해 시도했습니다./homeaccess denied

find /home -iname "*.pdf" | grep -v "access denied"

그러나 결과는 동일합니다. 이 줄을 어떻게 제거 할 수 있습니까?


답변:


19

access denied출력이 오류이며에 파이프되는 STDOUT 대신 STDERR로 전송되어 시도했기 때문에 작동하지 않았습니다 grep.

STDERR 만 리디렉션하여 이러한 오류가 표시되지 않도록 할 수 있습니다

find /home -iname "*.pdf" 2>/dev/null

또는 David Foerster가 언급했듯이 STDERR을 더 간결하게 닫을 수 있습니다.

find /home -iname "*.pdf" 2>&-

그러나 실제로 다른 사용자보다는 집을 검색하고 싶기 때문에 실제로 원하는 경우가 있습니다.

find ~ -iname "*.pdf"

오류가 발생하면 로컬 구성에 잘못된 소유권이있을 수 있으므로 조사해야합니다.


3
Grrr, 왜 사람들은 항상 30 초나 나를 때리나요? : \
귀하는 AGitForNotUsingGit

찾기 : "/home/ihsan/.gvfs": 액세스 거부 찾기 : "/home/ihsan/.dbus": ~ ~
solfish

뭔가 잘못 됐나요? 네, 또한 다른 사용자들에게 홈 디렉토리를 테스트하고 싶습니다
solfish

2
@solfish 내가 아는 한 그 파일은 당신이 소유해야합니다. 당신은 할 수 있습니다sudo chown $USER: ~/.gvfs ~/.dbus
ZANNA

1
로 stderr을 닫는 것으로 충분합니다 2>&-. 기능 장애 파일 디스크립터에 오류 메시지를 쓰려고하면 GNU find가 자체 종료되지 않습니다. 소유권 문제 sudo chown -R $USER: ...는에서 소유하지 않은 파일이 더 많은 경우 더 효과적입니다 $USER.
David Foerster

8

액세스 거부가 stderr아닌 에 인쇄 될 수 stdout있습니다.

이 시도:

find /home -iname "*.pdf" 2>&1 | grep -v "access denied"

2>&1의 출력 리디렉션 stderr에를 stdout그 때문에, grep -v그 일을 할 수 있습니다. (기본적으로 |파이프 만 stdout있고 stderr)


그러나이 2> & 1의 경우 stderr가 존재하면 stdout으로 보내시겠습니까?
solfish

@solfish 예, 그게 정확히 요점입니다 :)
당신은 AGitForNotUsingGit 오전

내가 이해하지 못하는 것은 "|"앞에있다 출력으로서; 우린 그냥 stderr있어? "|"뒤에 입력으로 우리는 이것을 얻었다
solfish

@solfish 글쎄, 나는 약 1 년 반 전에이 문제에 부딪 쳤고 다른 방법 으로 문제를 해결할 수있었습니다 . 그러나 내 대답 아래의 의견 은 단순히 사용하는 것이 좋습니다 2>&1... 나는 배쉬 전문가가 아니므로 잘못된 경우 다음과 같이 말하십시오 :)
You'reAGitForNotUsingGit

@AndroidDev 대안 으로이 답변에 다른 방법을 추가하는 것이 좋습니다. Etan Reisner의 비판프로세스 대체 가 이식성이 없다는 것입니다. 그러나 POSIX 모드를bash 제외하고 우분투에는 있습니다 . 나는 그것이 최선의 해결책 이라고 생각합니다 -악의적으로 명명 된 파일 access denied이 여전히 나타납니다.
엘리아 카간

4

당신은 아마 평균 무엇인가 - 어떤 "사용 권한이 거부되었습니다" 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경우에도 여전히 유용한 결과가 반환되어 작동하는 것처럼 보일 수 있습니다. 예를 들어, ac존재하지만 존재 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.23.3.2.3 절도 참조하십시오 .

비정상적인 파일 이름에 대한 위의 논의는 GNU find 와 관련 이 있으며, 이는 findUbuntu를 포함한 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위 대신 사용 됩니다.

이것은 교환하려는 두 표준 스트림 중 하나를 유지하기 위해 새 파일 디스크립터 를 도입 하고,이를 재 지정하기 위해 경로 재 지정을 수행하고, 새 파일 디스크립터를 닫아 작동합니다.

  • 파일 디스크립터 1은 stdout이고 2는 stderr입니다 (그리고 경로 재 지정되지 않은 0은 stdin ). 그러나 다른 파일 설명자를 사용하여 리디렉션 할 수도 있습니다. 파일이나 장치를 열거 나 열어 두는 데 사용할 수 있습니다.
  • 3>&1 stdout (파일 설명자 1)이 나중에 리디렉션 될 때 원래 stdout을 쉽게 쓸 수 있도록 파일 설명자 3을 stdout으로 리디렉션합니다.
  • 1>&2stdout을 stderr로 리디렉션합니다. 파일 디스크립터 3은 여전히 ​​원래 stdout이므로 여전히 액세스 할 수 있습니다.
  • 2>&3 stderr을 원래 stdout 인 파일 디스크립터 3으로 경로 재 지정합니다.
  • 3>&- 더 이상 필요하지 않은 파일 설명자 3을 닫습니다.
  • 자세한 내용은 stdout이 아닌 stderr를 파이프하는 방법을 참조하십시오 . IO 재 지정 - 표준 출력과 표준 에러 (고급) 스와핑 특히 필터를 통해서만 표준 오류 파이프를 .

그러나이 방법은 검색 결과가 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를 포함한 일부 셸 을 사용하면 명령을 실행하고 해당 스트림 중 하나에서 리디렉션 할 수있는 프로세스 대체 를 수행 할 수 있습니다. 당신은 리디렉션 할 수 있습니다 findA와 명령의 표준 오류 grep를 필터링 명령, 그 리디렉션 grep표준 오류에 명령의 표준 출력을.

find args 2> >(grep -Fv 'Permission denied' >&2)

크레딧은 이 아이디어 를 위해 Android Dev 로갑니다 .

프로세스 대체를 bash 지원 하지만 shUbuntudash그렇지 않습니다. stdout과 stderr를 바꾸는 방법은 여전히 ​​작동하지만이 방법을 사용하려고하면 "구문 오류 : 리디렉션이 예기치 않게 나타납니다." 또한 POSIX 모드bash 에서 실행 하면 프로세스 대체에 대한 지원이 해제됩니다.

bashPOSIX 모드 에서 실행되는 한 가지 상황 은 sh1 로 호출 될 때 입니다. 따라서 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.