Akses Mengambil Akses ditolak "

9

Ketika saya gunakan finduntuk melihat semua file pdf dalam /homedirektori, saya melihat access denied. Untuk menghilangkannya saya mencoba:

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

Namun hasilnya sama. Bagaimana saya bisa menghilangkan garis-garis ini?

solfish
sumber

Jawaban:

19

Apa yang Anda coba tidak berhasil karena access deniedoutputnya adalah kesalahan dan dikirim pada STDERR bukan STDOUT yang disalurkan ke grep.

Anda dapat menghindari melihat kesalahan-kesalahan itu dengan hanya mengarahkan STDERR

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

Atau seperti komentar David Foerster, kita dapat menutup STDERR secara lebih ringkas

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

Namun, saya curiga sebenarnya Anda hanya ingin mencari di rumah Anda daripada pengguna lain, jadi mungkin Anda benar-benar menginginkannya

find ~ -iname "*.pdf"

Jika itu menimbulkan kesalahan, mungkin ada beberapa kepemilikan yang salah dalam konfigurasi lokal Anda, yang harus Anda selidiki.

Zanna
sumber
3
Grrr, mengapa orang selalu mengalahkanku dalam 30 detik? : \
You'reAGitForNotUsingGit
find: "/home/ihsan/.gvfs": akses ditolak find: "/home/ihsan/.dbus": akses ditolak, untuk perintah ~
solfish
apakah ada yang salah dengan itu? ya saya juga ingin direktori home pengguna lain yang juga dibuat oleh saya untuk menguji
solfish
2
@ Wolfish Sejauh yang saya tahu file-file itu harus dimiliki oleh Anda. Anda mungkin inginsudo chown $USER: ~/.gvfs ~/.dbus
Zanna
1
Itu harus cukup untuk menutup stderr dengan 2>&-. GNU find tidak akan berhenti sendiri jika ia mencoba menulis pesan kesalahan ke deskriptor file yang tidak berfungsi. Untuk masalah kepemilikan sudo chown -R $USER: ...akan lebih efektif jika ada lebih banyak file di dalam yang tidak dimiliki oleh $USER.
David Foerster
8

Akses yang ditolak mungkin sedang dicetak stderrbukan stdout.

Coba ini:

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

The 2>&1pengalihan output dari stderrke stdout, sehingga grep -vdapat melakukan tugasnya. (Secara default, |hanya pipa stdoutdan tidak stderr.)

You'reAGitForNotUsingGit
sumber
tetapi untuk ini 2> & 1 berarti jika stderr ada, kirim ke stdout?
solfish
@solfish Yup, itulah intinya :)
You'reAGitForNotUsingGit
apa yang saya tidak mengerti adalah sebelum "|" sebagai output; kita punya stderr kan? dan setelah "|" sebagai masukan, kami mendapatkan ini
solfish
@ Wolfish Yah, saya mengalami masalah ini sekitar satu setengah tahun yang lalu, dan saya bisa memperbaikinya menggunakan metode yang berbeda . Tapi kemudian komentar di bawah jawaban saya menyarankan untuk hanya menggunakan 2>&1... Saya bukan pakar bash, jadi jika itu salah maka tolong katakan demikian :)
You'reAGitForNotUsingGit
@AndroidDev Saya sarankan menambahkan metode yang berbeda untuk jawaban ini sebagai alternatif. Kritik Etan Reisner adalah bahwa proses substitusi tidak mudah dibawa-bawa. Tetapi bashdi Ubuntu memilikinya, kecuali dalam mode POSIX . Saya pikir itu solusi terbaik - file yang bernama berbahaya access deniedakan tetap muncul.
Eliah Kagan
4

Anda mungkin berarti "Izin ditolak" —yang findditunjukkan oleh Ubuntu ketika Anda tidak dapat mengakses sesuatu karena izin file — alih-alih "akses ditolak".

Satu perintah yang sepenuhnya umum yang melakukan ini dengan benar (dan, sebagai bonus, mudah dibawa ke * nix lain , selama pesan kesalahannya sama) adalah:

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

(Biasanya Anda ingin menyampaikan beberapa argumen find. Itu sebelum pengalihan pertama 3>&1.)

Namun, seringkali Anda dapat menggunakan sesuatu yang lebih sederhana. Misalnya, Anda mungkin dapat menggunakan subtitusi proses . Detailnya mengikuti.

Metode Paling Umum dan Keterbatasannya

Dua pendekatan yang umum adalah membuang stderr (seperti dalam jawaban Zanna ) atau untuk mengarahkan stderr ke stdout dan memfilter stdout (seperti pada jawaban Android Dev ). Meskipun mereka memiliki keuntungan karena mudah ditulis dan sering merupakan pilihan yang masuk akal, pendekatan ini tidak ideal.

Membuang semua yang dikirim ke stderr —seperti dengan mengarahkannya ke perangkat nol dengan 2>/dev/nullatau dengan menutupnya 2>&-—memunculkan risiko kesalahan yang hilang selain "Izin ditolak".

"Izin ditolak" mungkin merupakan kesalahan paling umum yang terlihat saat berjalan find, tetapi itu jauh dari satu-satunya kesalahan yang mungkin terjadi, dan jika kesalahan lain terjadi, Anda mungkin ingin tahu tentang itu. Secara khusus, findmelaporkan "Tidak ada file atau direktori" jika titik awal tidak ada. Dengan beberapa titik awal, findmasih dapat mengembalikan beberapa hasil yang bermanfaat dan tampaknya berfungsi. Sebagai contoh, jika adan cada tetapi btidak, find a b c -name xcetakan hasil a, maka "Tidak ada berkas atau direktori" untuk b, maka hasil dalam c.

Menggabungkan stdout dan stderr bersama menjadi stdout dan mengirimnya ke grepatau perintah lain untuk memfilternya — seperti dengan 2>&1 | grep ...atau |& grep ...—membuat risiko secara tidak sengaja menyaring file yang namanya berisi pesan yang disaring.

Misalnya, jika Anda memfilter baris yang berisi "Izin ditolak" maka Anda juga akan menghapus hasil pencarian yang menampilkan nama file seperti "Izin ditolak messages.txt". Ini mungkin akan terjadi secara tidak sengaja, meskipun mungkin juga file diberi nama yang dibuat khusus untuk menggagalkan pencarian Anda.

Memfilter aliran gabungan memiliki masalah lain, yang tidak dapat dikurangi dengan menyaring lebih selektif (seperti dengan grep -vx 'find: .*: Permission denied'di sisi kanan pipa). Beberapa findtindakan, termasuk -printtindakan yang tersirat ketika Anda menentukan tidak ada tindakan, menentukan bagaimana cara menghasilkan nama file berdasarkan apakah stdout adalah terminal atau tidak .

  • Jika itu bukan terminal, maka nama file adalah output apa adanya bahkan jika mereka mengandung karakter aneh seperti baris baru dan karakter kontrol yang dapat mengubah perilaku terminal Anda. Jika ini adalah terminal, maka karakter-karakter ini ditekan dan ?dicetak sebagai gantinya.
  • Ini biasanya yang Anda inginkan. Jika Anda akan memproses nama file lebih lanjut, mereka harus di-output secara harfiah. Namun, jika Anda akan menampilkannya, nama file dengan baris baru dapat meniru beberapa nama file, dan nama file dengan urutan karakter backspace dapat muncul dengan nama yang berbeda. Masalah lain juga mungkin terjadi, seperti nama file yang mengandung urutan pelarian yang mengubah warna di terminal Anda.
  • Tetapi memipet hasil pencarian melalui perintah lain (seperti grep) menyebabkan findtidak lagi melihat terminal. (Lebih tepatnya, ini menyebabkan stdout tidak menjadi terminal.) Kemudian karakter aneh di-output secara literal. Tetapi jika semua perintah yang dilakukan di sisi kanan pipa adalah (a) menghapus baris yang tampak seperti pesan "Izin ditolak" dan (b) mencetak apa yang tersisa, maka Anda masih harus tunduk pada jenis shenanigans yang ada finddi terminal deteksi dimaksudkan untuk mencegah.
  • Lihat bagian FILENAMEN TIDAK BIASA man finduntuk informasi lebih lanjut, termasuk perilaku setiap tindakan yang mencetak nama file. ( "Banyak tindakan menemukan hasil dalam pencetakan data yang berada di bawah kendali pengguna lain ..." ) Lihat juga bagian 3.3.2.1 , 3.3.2.2 , dan 3.3.2.3 dari manual referensi GNU Findutils .

Diskusi di atas tentang nama file yang tidak biasa berkaitan dengan penemuan GNU , yang merupakan findimplementasi dalam sistem GNU / Linux termasuk Ubuntu.

Meninggalkan Output Standar Sendiri Saat Memfilter Kesalahan Standar

Apa yang benar - benar Anda inginkan di sini adalah membiarkan stdout tetap utuh sementara mem- piping stderr ke grep. Sayangnya tidak ada sintaksis sederhana untuk ini. |pipa stdout, dan beberapa cangkang (termasuk bash) mendukung |&pipa kedua aliran — atau Anda dapat mengarahkan stderr ke stdout terlebih dahulu 2>&1 |, yang memiliki efek yang sama. Tetapi cangkang yang biasa digunakan tidak memberikan sintaks untuk pipa stderr saja.

Anda masih bisa melakukan ini. Canggung saja. Salah satu caranya adalah dengan menukar stdout dengan stderr , sehingga hasil pencarian berada di stderr dan kesalahan ada di stdout, kemudian pipa stdout ke grepuntuk pemfilteran:

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

Biasanya Anda akan meneruskan argumen find, seperti titik awal (tempat untuk mencari, yang biasanya merupakan direktori) dan predikat (tes dan tindakan). Ini menggantikan tempat di argsatas.

Ini berfungsi dengan memperkenalkan deskriptor file baru untuk menahan salah satu dari dua stream standar yang ingin Anda swap, melakukan pengalihan untuk menukar mereka, dan menutup deskriptor file baru.

  • Deskriptor file 1 adalah stdout dan 2 adalah stderr (dan 0 yang tidak diarahkan adalah stdin ). Tetapi Anda juga dapat mengarahkan ulang menggunakan deskriptor file lain. Ini dapat digunakan untuk membuka, atau tetap membuka, file atau perangkat.
  • 3>&1 mengarahkan file deskriptor 3 ke stdout, sehingga ketika stdout (file deskriptor 1) kemudian diarahkan, stdout asli masih dapat ditulis dengan mudah.
  • 1>&2mengarahkan ulang stdout ke stderr. Karena file descriptor 3 masih stdout asli, itu masih dapat diakses.
  • 2>&3 mengarahkan ulang stderr ke file descriptor 3, yang merupakan stdout asli.
  • 3>&- menutup file deskriptor 3, yang tidak lagi diperlukan.
  • Untuk informasi lebih lanjut, lihat Bagaimana memasang pipa stderr, dan bukan stdout? dan IO Redirection - Swapping stdout dan stderr (Advanced) dan khususnya pipa hanya stderr melalui filter .

Namun, metode ini memiliki kelemahan yaitu hasil pencarian dikirim ke stderr dan kesalahan dikirim ke stdout . Jika Anda menjalankan perintah ini secara langsung dalam shell interaktif dan tidak memiplak atau mengarahkan output lebih jauh, maka itu tidak terlalu penting. Kalau tidak, itu bisa menjadi masalah. Jika Anda menempatkan perintah itu dalam skrip, dan kemudian seseorang (mungkin Anda, nanti) mengalihkan atau mem-pipe output-nya, itu tidak berlaku seperti yang diharapkan .

Solusinya adalah dengan menukar stream kembali setelah Anda selesai memfilter output . Menerapkan pengalihan yang sama seperti yang ditunjukkan di atas di sisi kanan pipa tidak akan mencapai ini, karena |hanya pipa stdout, sehingga sisi pipa hanya menerima output yang awalnya dikirim ke stderr (karena aliran ditukar) dan bukan yang asli keluaran stdout. Sebagai gantinya, Anda dapat menggunakan ( )untuk menjalankan perintah di atas dalam sebuah subkulit ( terkait ), kemudian menerapkan pengalihan swapping untuk itu:

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

Pengelompokan, bukan secara khusus subkulit, yang membuat ini bekerja. Jika suka, Anda dapat menggunakan { ;}:

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

A Way Kurang rumit: Proses Substitusi

Beberapa shell, termasuk Bash pada sistem yang dapat mendukungnya (termasuk sistem GNU / Linux seperti Ubuntu), memungkinkan Anda melakukan substitusi proses , yang memungkinkan Anda untuk menjalankan perintah dan mengarahkan ke / dari salah satu stream-nya. Anda dapat mengarahkan findstderr perintah ke grepperintah yang memfilternya, dan mengarahkan grepstdout perintah itu ke stderr.

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

Kredit diberikan ke Android Dev untuk ide ini.

Meskipun bash mendukung proses substitusi, shdi Ubuntu adalah dash, yang tidak. Ini akan memberi Anda "Kesalahan sintaks: pengalihan tidak terduga" jika Anda mencoba menggunakan metode ini, sedangkan metode swapping stdout dan stderr akan tetap berfungsi. Selanjutnya, ketika bashberjalan dalam mode POSIX , dukungan untuk substitusi proses dimatikan.

Satu situasi di mana bashberjalan dalam mode POSIX adalah ketika dipanggil sebagai sh1 . Oleh karena itu, pada OS seperti Fedora mana bashmenyediakan /bin/sh, atau jika Anda telah membuat /bin/shtitik symlink untuk bashdiri sendiri di Ubuntu, proses substitusi masih tidak berfungsi dalam shskrip, tanpa perintah sebelumnya untuk mematikan mode POSIX. Taruhan terbaik Anda, jika Anda ingin menggunakan metode ini dalam skrip, adalah menempatkan #!/bin/bash di atas, bukan #!/bin/sh, jika Anda belum melakukannya.

1 : Dalam situasi ini, bashaktifkan mode POSIX secara otomatis setelah menjalankan perintah dalam skrip startupnya.

Sebuah contoh

Sangat berguna untuk dapat menguji perintah-perintah ini. Untuk melakukan ini, saya membuat tmpsubdirektori dari direktori saat ini dan mengisinya dengan beberapa file dan direktori, mengambil izin dari salah satu dari mereka untuk memicu kesalahan "Izin ditolak" di 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

Salah satu direktori yang dapat diakses termasuk file dengan "Izin ditolak" dalam namanya. Berjalan findtanpa pengalihan atau pipa menunjukkan file ini, tetapi juga menunjukkan kesalahan "Izin ditolak" sebenarnya untuk direktori lain yang tidak dapat diakses:

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

Memipilkan stdout dan stderr ke grepdan memfilter baris yang berisi "Izin ditolak" membuat pesan kesalahan hilang tetapi juga menyembunyikan hasil pencarian untuk file dengan frasa dalam namanya:

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

find 2>&1 | grep -Fv 'Permission denied' sama dan menghasilkan output yang sama.

Metode yang ditunjukkan di atas untuk memfilter "Izin ditolak" hanya dari pesan kesalahan — dan bukan dari hasil pencarian — berhasil. Sebagai contoh, inilah metode di mana stdout dan stderr bertukar:

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) menghasilkan output yang sama.

Anda dapat memicu pesan kesalahan yang berbeda untuk memastikan bahwa baris yang dikirim ke stderr yang tidak mengandung teks "Izin ditolak" masih diizinkan. Sebagai contoh, di sini saya telah menjalankan finddengan direktori saat ini ( .) sebagai satu titik awal, tetapi direktori tidak ada foosebagai yang lain:

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

Memeriksa Itu findOutput Standar Masih Terminal

Kita juga dapat melihat perintah mana yang menyebabkan karakter khusus, seperti baris baru, untuk ditampilkan secara literal. (Ini dapat dilakukan secara terpisah dari demonstrasi di atas, dan itu tidak perlu ada di tmpdirektori.)

Buat file dengan baris baru dalam namanya:

touch $'abc\ndef'

Biasanya kami menggunakan direktori sebagai titik awal find, tetapi file juga berfungsi:

$ find abc*
abc?def

Memutuskan stdout ke perintah lain menyebabkan baris baru akan ditampilkan secara harfiah, menciptakan kesan salah dari dua hasil pencarian terpisah abcdan def. Kami dapat mengujinya dengan cat:

$ find abc* | cat
abc
def

Mengarahkan just stderr tidak menyebabkan masalah ini:

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

Juga tidak menutupnya:

$ find abc* 2>&-
abc?def

Pipa untuk grep melakukan penyebabnya masalah:

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

(Mengganti |&dengan 2>&1 |setara dan menghasilkan output yang sama.)

Bertukar stdout dan stderr dan perpipaan stdout tidak menyebabkan masalah— findstdout menjadi stderr, yang tidak disalurkan:

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

Mengelompokkan perintah itu dan menukar stream kembali tidak menyebabkan masalah:

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

( { ;}Versi ini menghasilkan output yang sama.)

Menggunakan substitusi proses untuk memfilter stderr juga tidak menyebabkan masalah:

$ find abc* 2> >(grep -Fv 'Permission denied' >&2)
abc?def
Eliah Kagan
sumber