Anda mungkin berarti "Izin ditolak" —yang find
ditunjukkan 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/null
atau 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, find
melaporkan "Tidak ada file atau direktori" jika titik awal tidak ada. Dengan beberapa titik awal, find
masih dapat mengembalikan beberapa hasil yang bermanfaat dan tampaknya berfungsi. Sebagai contoh, jika a
dan c
ada tetapi b
tidak, find a b c -name x
cetakan hasil a
, maka "Tidak ada berkas atau direktori" untuk b
, maka hasil dalam c
.
Menggabungkan stdout dan stderr bersama menjadi stdout dan mengirimnya ke grep
atau 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 find
tindakan, termasuk -print
tindakan 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 find
tidak 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 find
di terminal deteksi dimaksudkan untuk mencegah.
- Lihat bagian FILENAMEN TIDAK BIASA
man find
untuk 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 find
implementasi 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 grep
untuk 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 args
atas.
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>&2
mengarahkan 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 find
stderr perintah ke grep
perintah yang memfilternya, dan mengarahkan grep
stdout 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, sh
di 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 bash
berjalan dalam mode POSIX , dukungan untuk substitusi proses dimatikan.
Satu situasi di mana bash
berjalan dalam mode POSIX adalah ketika dipanggil sebagai sh
1 . Oleh karena itu, pada OS seperti Fedora mana bash
menyediakan /bin/sh
, atau jika Anda telah membuat /bin/sh
titik symlink untuk bash
diri sendiri di Ubuntu, proses substitusi masih tidak berfungsi dalam sh
skrip, 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, bash
aktifkan 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 tmp
subdirektori 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 find
tanpa 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 grep
dan 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 find
dengan direktori saat ini ( .
) sebagai satu titik awal, tetapi direktori tidak ada foo
sebagai 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 find
Output 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 tmp
direktori.)
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 abc
dan 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— find
stdout 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