Ketika saya gunakan find
untuk melihat semua file pdf dalam /home
direktori, 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?
command-line
bash
find
solfish
sumber
sumber
Jawaban:
Apa yang Anda coba tidak berhasil karena
access denied
outputnya adalah kesalahan dan dikirim pada STDERR bukan STDOUT yang disalurkan kegrep
.Anda dapat menghindari melihat kesalahan-kesalahan itu dengan hanya mengarahkan STDERR
Atau seperti komentar David Foerster, kita dapat menutup STDERR secara lebih ringkas
Namun, saya curiga sebenarnya Anda hanya ingin mencari di rumah Anda daripada pengguna lain, jadi mungkin Anda benar-benar menginginkannya
Jika itu menimbulkan kesalahan, mungkin ada beberapa kepemilikan yang salah dalam konfigurasi lokal Anda, yang harus Anda selidiki.
sumber
sudo chown $USER: ~/.gvfs ~/.dbus
2>&-
. GNU find tidak akan berhenti sendiri jika ia mencoba menulis pesan kesalahan ke deskriptor file yang tidak berfungsi. Untuk masalah kepemilikansudo chown -R $USER: ...
akan lebih efektif jika ada lebih banyak file di dalam yang tidak dimiliki oleh$USER
.Akses yang ditolak mungkin sedang dicetak
stderr
bukanstdout
.Coba ini:
The
2>&1
pengalihan output daristderr
kestdout
, sehinggagrep -v
dapat melakukan tugasnya. (Secara default,|
hanya pipastdout
dan tidakstderr
.)sumber
2>&1
... Saya bukan pakar bash, jadi jika itu salah maka tolong katakan demikian :)bash
di Ubuntu memilikinya, kecuali dalam mode POSIX . Saya pikir itu solusi terbaik - file yang bernama berbahayaaccess denied
akan tetap muncul.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:
(Biasanya Anda ingin menyampaikan beberapa argumen
find
. Itu sebelum pengalihan pertama3>&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 menutupnya2>&-
—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, jikaa
danc
ada tetapib
tidak,find a b c -name x
cetakan hasila
, maka "Tidak ada berkas atau direktori" untukb
, maka hasil dalamc
.Menggabungkan stdout dan stderr bersama menjadi stdout dan mengirimnya ke
grep
atau perintah lain untuk memfilternya — seperti dengan2>&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). Beberapafind
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 .?
dicetak sebagai gantinya.grep
) menyebabkanfind
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 adafind
di terminal deteksi dimaksudkan untuk mencegah.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 (termasukbash
) mendukung|&
pipa kedua aliran — atau Anda dapat mengarahkan stderr ke stdout terlebih dahulu2>&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:Biasanya Anda akan meneruskan argumen
find
, seperti titik awal (tempat untuk mencari, yang biasanya merupakan direktori) dan predikat (tes dan tindakan). Ini menggantikan tempat diargs
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.
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.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:Pengelompokan, bukan secara khusus subkulit, yang membuat ini bekerja. Jika suka, Anda dapat menggunakan
{
;}
: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 kegrep
perintah yang memfilternya, dan mengarahkangrep
stdout perintah itu ke stderr.Kredit diberikan ke Android Dev untuk ide ini.
1>&2
. Saya telah menggunakan>&2
, yang setara ( terkait ). Gunakan mana yang Anda suka.Meskipun
bash
mendukung proses substitusi,sh
di Ubuntu adalahdash
, 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, ketikabash
berjalan dalam mode POSIX , dukungan untuk substitusi proses dimatikan.Satu situasi di mana
bash
berjalan dalam mode POSIX adalah ketika dipanggil sebagaish
1 . Oleh karena itu, pada OS seperti Fedora manabash
menyediakan/bin/sh
, atau jika Anda telah membuat/bin/sh
titik symlink untukbash
diri sendiri di Ubuntu, proses substitusi masih tidak berfungsi dalamsh
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" difind
.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: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: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:
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 adafoo
sebagai yang lain:Memeriksa Itu
find
Output Standar Masih TerminalKita 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:
Biasanya kami menggunakan direktori sebagai titik awal
find
, tetapi file juga berfungsi:Memutuskan stdout ke perintah lain menyebabkan baris baru akan ditampilkan secara harfiah, menciptakan kesan salah dari dua hasil pencarian terpisah
abc
dandef
. Kami dapat mengujinya dengancat
:Mengarahkan just stderr tidak menyebabkan masalah ini:
Juga tidak menutupnya:
Pipa untuk
grep
melakukan penyebabnya masalah:(Mengganti
|&
dengan2>&1 |
setara dan menghasilkan output yang sama.)Bertukar stdout dan stderr dan perpipaan stdout tidak menyebabkan masalah—
find
stdout menjadi stderr, yang tidak disalurkan:Mengelompokkan perintah itu dan menukar stream kembali tidak menyebabkan masalah:
(
{
;}
Versi ini menghasilkan output yang sama.)Menggunakan substitusi proses untuk memfilter stderr juga tidak menyebabkan masalah:
sumber