Lấy ra 'Truy cập bị từ chối "Dòng


9

Khi tôi sử dụng findđể xem tất cả các tệp pdf trong /homethư mục, tôi đang thấy access denied. Để loại bỏ chúng, tôi đã thử:

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

Tuy nhiên, kết quả là như nhau. Làm thế nào tôi có thể thoát khỏi những dòng này?


Câu trả lời:


19

Những gì bạn đã thử không hoạt động vì access deniedđầu ra là lỗi và được gửi trên STDERR thay vì STDOUT được chuyển đến grep.

Bạn có thể tránh nhìn thấy những lỗi đó bằng cách chỉ chuyển hướng STDERR

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

Hoặc như David Foerster nhận xét, chúng ta có thể đóng STTERR ngắn gọn hơn

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

Tuy nhiên, tôi nghi ngờ bạn thực sự chỉ muốn tìm kiếm nhà của bạn chứ không phải người dùng khác, vì vậy có lẽ bạn thực sự muốn

find ~ -iname "*.pdf"

Nếu điều đó ném lỗi, có thể có một số quyền sở hữu sai trong cấu hình cục bộ của bạn, mà bạn nên điều tra.


3
Grrr, tại sao mọi người luôn đánh bại tôi trong 30 giây? : \
AreAGitForNotUsingGit

tìm thấy: “/home/ihsan/.gvfs”: access denied tìm thấy: “/home/ihsan/.dbus”: Truy cập bị từ chối, cho lệnh ~
solfish

Có điều gì sai cho điều đó? vâng tôi cũng muốn thư mục nhà của người dùng khác cũng được tôi tạo ra để kiểm tra
solfish

2
@solfish Theo tôi biết những tập tin đó nên thuộc sở hữu của bạn. Bạn có thể muốnsudo chown $USER: ~/.gvfs ~/.dbus
Zanna

1
Nó là đủ để đóng stderr với 2>&-. GNU find sẽ không tự chấm dứt nếu nó cố gắng viết các thông báo lỗi vào một bộ mô tả tệp bị rối loạn. Đối với các vấn đề quyền sở hữu sudo chown -R $USER: ...sẽ có hiệu quả hơn trong trường hợp có nhiều tệp trong đó không thuộc sở hữu của $USER.
David Foerster

8

Truy cập bị từ chối có lẽ được in trên stderrchứ không phải stdout.

Thử cái này:

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

Các 2>&1chuyển hướng đầu ra từ stderrđến stdout, để grep -vcó thể thực hiện công việc của mình. (Theo mặc định, |chỉ có đường ống stdoutvà không stderr.)


nhưng đối với điều này 2> & 1 có nghĩa là nếu stderr tồn tại gửi đến thiết bị xuất chuẩn?
solfish

@solfish Yup, đó chính xác là vấn đề :)
You'SAGitForNotUsingGit

những gì tôi không hiểu là trước "|" như một đầu ra; chúng ta chỉ có stderr phải không? và sau "|" như một đầu vào, chúng tôi đã nhận được điều này
solfish

@solfish Chà, tôi đã gặp phải vấn đề này khoảng một năm rưỡi trước và tôi đã có thể khắc phục bằng một phương pháp khác . Nhưng sau đó, một bình luận bên dưới câu trả lời của tôi đã đề nghị sử dụng đơn giản 2>&1... Tôi không phải là chuyên gia bash, vì vậy nếu điều đó không chính xác thì xin vui lòng nói như vậy :)
You'reAGitForNotUsingGit

@AndroidDev Tôi đề nghị thêm phương pháp khác vào câu trả lời này để thay thế. Chỉ trích Etan Reisner củaquá trình thay thế là không cầm tay. Nhưng bashtrong Ubuntu có nó, ngoại trừ trong chế độ POSIX . Tôi nghĩ đó là giải pháp tốt nhất - một tệp có tên độc hại access deniedvẫn sẽ xuất hiện.
Eliah Kagan

4

Bạn có thể có nghĩa là "Quyền bị từ chối", đó là những gì findtrong Ubuntu hiển thị cho bạn khi bạn không thể truy cập một cái gì đó vì quyền truy cập tệp chứ không phải là "quyền truy cập bị từ chối".

Một lệnh hoàn toàn chung thực hiện chính xác điều này (và, như một phần thưởng, có thể được chuyển sang * nix es khác, miễn là thông báo lỗi giống nhau) là:

(find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-

(Thông thường bạn muốn chuyển một số đối số cho find. Những người đi trước khi chuyển hướng đầu tiên 3>&1.)

Tuy nhiên, thường bạn sẽ có thể sử dụng một cái gì đó đơn giản hơn. Ví dụ, bạn có thể sử dụng thay thế quá trình . Chi tiết theo dõi.

Các phương pháp phổ biến nhất và những hạn chế của chúng

Hai cách tiếp cận điển hình là loại bỏ stderr (như trong câu trả lời của Zanna ) hoặc chuyển hướng stderr sang thiết bị xuất chuẩn và lọc thiết bị xuất chuẩn (như trong câu trả lời của Android Dev ). Mặc dù chúng có ưu điểm là viết đơn giản và thường là lựa chọn hợp lý, những cách tiếp cận này không lý tưởng.

Loại bỏ tất cả mọi thứ được gửi tới thiết bị stderr bằng cách chuyển hướng nó sang thiết bị null2>/dev/nullhoặc bằng cách đóng nó với 2>&-siêu tốc có nguy cơ bị thiếu các lỗi khác ngoài "Quyền bị từ chối".

"Quyền bị từ chối" có thể là lỗi phổ biến nhất được thấy khi chạy find, nhưng nó khác xa với lỗi duy nhất có thể xảy ra và nếu xảy ra lỗi khác, bạn có thể muốn biết về nó. Cụ thể, findbáo cáo "Không có tệp hoặc thư mục như vậy" nếu điểm bắt đầu không tồn tại. Với nhiều điểm bắt đầu, findvẫn có thể trả về một số kết quả hữu ích và có vẻ hoạt động. Ví dụ: nếu actồn tại nhưng bkhông, find a b c -name xin kết quả a, sau đó "Không có tệp hoặc thư mục như vậy" cho b, sau đó kết quả c.

Kết hợp thiết bị xuất chuẩn và thiết bị xuất chuẩn với nhau thành thiết bị xuất chuẩn và chuyển nó tới grephoặc một số lệnh khác để lọc nó như với 2>&1 | grep ...hoặc |& grep ...đối với nguy cơ vô tình lọc ra một tệp có tên chứa thông điệp được lọc.

Ví dụ: nếu bạn lọc ra các dòng có chứa "Quyền bị từ chối" thì bạn cũng sẽ bỏ kết quả tìm kiếm hiển thị tên tệp như "Quyền bị từ chối Messages.txt". Điều này có thể xảy ra một cách tình cờ, mặc dù cũng có thể một tập tin sẽ được đặt một cái tên được chế tạo đặc biệt để cản trở các tìm kiếm của bạn.

Lọc các luồng kết hợp có một vấn đề khác, không thể giảm thiểu bằng cách lọc có chọn lọc hơn (chẳng hạn như với grep -vx 'find: .*: Permission denied'bên phải của đường ống). Một số findhành động, bao gồm cả -printhành động tiềm ẩn khi bạn chỉ định không có hành động, xác định cách xuất tên tệp dựa trên việc thiết bị xuất chuẩn có phải là thiết bị đầu cuối hay không .

  • Nếu đó không phải là một thiết bị đầu cuối, thì tên tệp được xuất ra ngay cả khi chúng chứa các ký tự lạ như dòng mới và ký tự điều khiển có thể thay đổi hành vi của thiết bị đầu cuối của bạn. Nếu nó một thiết bị đầu cuối, thì các ký tự này bị chặn và ?được in thay thế.
  • Đây thường là những gì bạn muốn. Nếu bạn định xử lý tên tệp hơn nữa, chúng phải được xuất ra theo nghĩa đen. Tuy nhiên, nếu bạn định hiển thị chúng, một tên tệp có dòng mới có thể bắt chước nhiều tên tệp và một tên tệp có một chuỗi các ký tự backspace có thể là một tên khác. Các vấn đề khác cũng có thể xảy ra, chẳng hạn như tên tệp chứa trình tự thoát làm thay đổi màu sắc trong thiết bị đầu cuối của bạn.
  • Nhưng đường ống kết quả tìm kiếm thông qua một lệnh khác (như grep) khiến findkhông còn thấy thiết bị đầu cuối. (Chính xác hơn, nó làm cho thiết bị xuất chuẩn của nó không phải là một thiết bị đầu cuối.) Sau đó, các ký tự lạ được xuất ra theo nghĩa đen. Nhưng nếu tất cả lệnh ở phía bên phải của đường ống là (a) xóa các dòng trông giống như thông báo "Quyền bị từ chối" và (b) in những gì còn lại, thì bạn vẫn phải tuân theo loại findthiết bị đầu cuối của shenanigans phát hiện nhằm mục đích ngăn chặn.
  • Xem phần PHIM TUYỆT VỜI man findđể biết thêm thông tin, bao gồm cả hành vi của từng hành động in tên tệp. ( "Nhiều hành động tìm kết quả trong việc in dữ liệu nằm dưới sự kiểm soát của người dùng khác ..." ) Xem thêm các phần 3.3.2.1 , 3.3.2.23.3.2.3 của tài liệu tham khảo GNU Findutils .

Các cuộc thảo luận ở trên về tên tệp bất thường liên quan đến GNU find , đó là việc findtriển khai trong các hệ thống GNU / Linux bao gồm Ubuntu.

Rời khỏi đầu ra tiêu chuẩn một mình trong khi lọc lỗi tiêu chuẩn

Những gì bạn thực sự muốn ở đây là để lại stdout nguyên vẹn trong khi đường ống stderr đến grep. Thật không may, không có cú pháp đơn giản cho việc này. |ống stdout và một số shell (bao gồm bash) hỗ trợ |&cho cả hai luồng Stream hoặc bạn có thể chuyển hướng stderr sang stdout trước 2>&1 |, có tác dụng tương tự. Nhưng các shell thường được sử dụng không chỉ cung cấp một cú pháp cho stderr ống.

Bạn vẫn có thể làm điều này. Nó chỉ là vụng về. Một cách là hoán đổi thiết bị xuất chuẩn với thiết bị xuất chuẩn , để kết quả tìm kiếm nằm trên thiết bị xuất chuẩn và lỗi nằm trên thiết bị xuất chuẩn, sau đó đặt thiết bị xuất chuẩn để greplọc:

find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'

Thông thường, bạn sẽ chuyển các đối số đến find, chẳng hạn như điểm bắt đầu (nơi để tìm kiếm, thường là thư mục) và vị ngữ (kiểm tra và hành động). Những đi ở vị trí argstrên.

Điều này hoạt động bằng cách giới thiệu một bộ mô tả tệp mới để giữ một trong hai luồng tiêu chuẩn bạn muốn trao đổi, thực hiện chuyển hướng để hoán đổi chúng và đóng bộ mô tả tệp mới.

  • Mô tả tệp 1 là thiết bị xuất chuẩn và 2 là thiết bị xuất chuẩn (và 0 không được xác nhận là stdin ). Nhưng bạn cũng có thể chuyển hướng bằng cách sử dụng các mô tả tập tin khác. Điều này có thể được sử dụng để mở, hoặc tiếp tục mở, một tập tin hoặc thiết bị.
  • 3>&1 chuyển hướng mô tả tệp 3 thành thiết bị xuất chuẩn, do đó, khi thiết bị xuất chuẩn (mô tả tệp 1) sau đó được chuyển hướng, thiết bị xuất chuẩn ban đầu vẫn có thể được ghi dễ dàng.
  • 1>&2chuyển hướng stdout đến stderr. Vì mô tả tệp 3 vẫn là thiết bị xuất chuẩn ban đầu, nên nó vẫn có thể được truy cập.
  • 2>&3 chuyển hướng stderr để mô tả tập tin 3, đó là thiết bị xuất chuẩn ban đầu.
  • 3>&- đóng mô tả tập tin 3, không còn cần thiết.
  • Để biết thêm thông tin, xem Làm thế nào để ống stderr, và không stdout? IO Redirection - Hoán đổi thiết bị xuất chuẩn và thiết bị xuất chuẩn (Nâng cao) và đặc biệt là ống chỉ stderr thông qua bộ lọc .

Tuy nhiên, phương pháp này có nhược điểm là kết quả tìm kiếm được gửi đến thiết bị lỗi chuẩn và lỗi được gửi đến thiết bị xuất chuẩn . Nếu bạn đang chạy lệnh này trực tiếp trong một vỏ tương tác và không chuyển hướng hoặc chuyển hướng đầu ra nữa, thì điều đó không thực sự quan trọng. Nếu không, nó có thể là một vấn đề. Nếu bạn đặt lệnh đó trong một tập lệnh, và sau đó ai đó (có thể là bạn, sau này) chuyển hướng hoặc chuyển đổi đầu ra của nó, thì nó không hoạt động như mong đợi .

Giải pháp là hoán đổi các luồng trở lại sau khi bạn hoàn thành việc lọc đầu ra . Áp dụng các chuyển hướng tương tự được hiển thị ở trên ở bên phải của đường ống sẽ không đạt được điều này, bởi vì |chỉ các ống dẫn ra, do đó, phía bên của đường ống chỉ nhận được đầu ra ban đầu được gửi đến stderr (vì các luồng bị tráo đổi) chứ không phải ban đầu đầu ra tiêu chuẩn. Thay vào đó, bạn có thể sử dụng ( )để chạy lệnh trên trong một lớp con ( có liên quan ), sau đó áp dụng các chuyển hướng hoán đổi cho điều đó:

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

Đó là nhóm, không cụ thể là subshell, làm cho công việc này. Nếu bạn thích, bạn có thể sử dụng { ;}:

{ find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'; } 3>&1 1>&2 2>&3 3>&-

Một cách ít rườm rà hơn: Thay thế quy trình

Một số hệ vỏ, bao gồm Bash trên các hệ thống có thể hỗ trợ nó (bao gồm cả các hệ thống GNU / Linux như Ubuntu), cho phép bạn thực hiện thay thế quy trình , cho phép bạn chạy một lệnh và chuyển hướng đến / từ một trong các luồng của nó. Bạn có thể chuyển hướng findstderr của lệnh thành một greplệnh lọc nó và chuyển hướng grepstdout của lệnh đó sang stderr.

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

Tín dụng cho Android Dev cho ý tưởng này.

Mặc dù bash hỗ trợ xử lý thay thế, shtrong Ubuntudash, đó không. Nó sẽ cung cấp cho bạn "Lỗi cú pháp: chuyển hướng bất ngờ" nếu bạn cố gắng sử dụng phương pháp này, trong khi phương pháp hoán đổi thiết bị xuất chuẩn và thiết bị xuất chuẩn vẫn sẽ hoạt động. Hơn nữa, khi bashchạy ở chế độ POSIX , hỗ trợ thay thế quy trình sẽ bị tắt.

Một tình huống khi bashchạy trong chế độ POSIX là khi nó được gọi là sh1 . Do đó, trên một hệ điều hành như Fedora bashcung cấp /bin/shhoặc nếu bạn đã tạo /bin/shđiểm liên kết tượng trưng cho bashchính mình trên Ubuntu, việc thay thế quy trình vẫn không hoạt động trong shtập lệnh, không có lệnh trước để tắt chế độ POSIX. Đặt cược tốt nhất của bạn, nếu bạn muốn sử dụng phương pháp này trong một tập lệnh, là đặt lên #!/bin/bash hàng đầu thay vì #!/bin/sh, nếu bạn chưa có.

1 : Trong tình huống này, bashtự động bật chế độ POSIX sau khi nó chạy các lệnh trong tập lệnh khởi động.

Một ví dụ

Nó rất hữu ích để có thể kiểm tra các lệnh này. Để làm điều này, tôi tạo một tmpthư mục con của thư mục hiện tại và điền vào đó một số tệp và thư mục, lấy quyền từ một trong số chúng để kích hoạt lỗi "Quyền bị từ chối" 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

Một trong những thư mục đó truy cập bao gồm một tập tin với "Permission denied" trong tên của nó. Chạy findkhông có chuyển hướng hoặc đường ống hiển thị tệp này, nhưng cũng hiển thị lỗi "Quyền bị từ chối" thực tế cho một thư mục khác không thể truy cập:

ek@Io:~/tmp$ find
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: ‘./b’: Permission denied

Đường ống cả stdout và stderr đến grepvà lọc ra các dòng có chứa "Quyền bị từ chối" làm cho thông báo lỗi biến mất nhưng cũng ẩn kết quả tìm kiếm cho tệp có cụm từ đó trong tên của nó:

ek@Io:~/tmp$ find |& grep -Fv 'Permission denied'
.
./a
./a/x
./c
./c/z
./w
./b

find 2>&1 | grep -Fv 'Permission denied' là tương đương và tạo ra cùng một đầu ra.

Các phương pháp được hiển thị ở trên để lọc ra "Quyền bị từ chối" chỉ từ các thông báo lỗi, chứ không phải từ kết quả tìm kiếm, thành công. Ví dụ: đây là phương thức mà thiết bị xuất chuẩn và thiết bị xuất chuẩn được hoán đổi:

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) tạo ra cùng một đầu ra.

Bạn có thể kích hoạt một thông báo lỗi khác để đảm bảo rằng các dòng được gửi tới thiết bị lỗi chuẩn không chứa văn bản "Quyền bị từ chối" vẫn được cho phép thông qua. Ví dụ, ở đây tôi đã chạy findvới thư mục hiện tại ( .) là một điểm bắt đầu, nhưng thư mục không tồn tại foonhư một điểm khác:

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

Kiểm tra findđầu ra tiêu chuẩn đó vẫn là một thiết bị đầu cuối

Chúng ta cũng có thể thấy lệnh nào gây ra các ký tự đặc biệt, chẳng hạn như dòng mới, được hiển thị theo nghĩa đen. (Điều này có thể được thực hiện tách biệt với phần trình diễn ở trên và không cần phải có trong tmpthư mục.)

Tạo một tập tin với một dòng mới trong tên của nó:

touch $'abc\ndef'

Thông thường chúng tôi sử dụng các thư mục làm điểm bắt đầu find, nhưng các tệp cũng hoạt động:

$ find abc*
abc?def

Đường ống dẫn đến một lệnh khác làm cho dòng mới được xuất ra theo nghĩa đen, tạo ra ấn tượng sai về hai kết quả tìm kiếm riêng biệt abcdef. Chúng tôi có thể kiểm tra điều đó với cat:

$ find abc* | cat
abc
def

Chuyển hướng chỉ stderr không gây ra vấn đề này:

$ find abc* 2>/dev/null
abc?def

Cũng không đóng nó:

$ find abc* 2>&-
abc?def

Đường ống để grep không gây ra vấn đề:

$ find abc* |& grep -Fv 'Permission denied'
abc
def

(Thay thế |&bằng 2>&1 |tương đương và tạo ra cùng một đầu ra.)

Hoán đổi thiết bị xuất chuẩn và thiết bị xuất chuẩn và thiết bị xuất chuẩn không làm cho vấn đề findthiết bị xuất chuẩn trở thành thiết bị xuất chuẩn, mà không phải là đường ống:

$ find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
abc?def

Nhóm lệnh đó và hoán đổi các luồng trở lại không gây ra vấn đề:

$ (find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
abc?def

( { ;}Phiên bản tạo ra cùng một đầu ra.)

Sử dụng thay thế quá trình để lọc stderr cũng không gây ra vấn đề:

$ find abc* 2> >(grep -Fv 'Permission denied' >&2)
abc?def
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.