Kecualikan direktori dalam pencarian cari

12

Pencarian dengan locatepath yang ditemukan di sistem file.
Seringkali, Anda tahu apriori bahwa Anda hanya tertarik pada file saja, atau direktori saja.
Pencarian 'cari' sering menghasilkan banyak hasil. Akan bermanfaat jika hanya memasukkan salah satu jenis dalam hasil, karena membantu mempersingkat output.

Tetapi ada argumen yang lebih menarik untuk meninggalkan file atau direktori: karena daftar jalur hasil bisa ambigu - tidak hanya dalam teori.

Contoh di bawah ini adalah kasus dunia nyata, dan bukan tidak biasa:

$ locate --regex --basename "xfce4-keyboard-overlay$"
/usr/local/bin/xfce4-keyboard-overlay
/usr/local/share/xfce4-keyboard-overlay

Ok, kami menemukan sesuatu! Tapi ... file, atau direktori?

$ file /usr/local/bin/xfce4-keyboard-overlay 
/usr/local/bin/xfce4-keyboard-overlay:   bash script

Jadi itu file ...

$ file /usr/local/share/xfce4-keyboard-overlay
/usr/local/share/xfce4-keyboard-overlay: directory

sedangkan yang kedua tidak.

Ambiguitas ini membuat daftar panjang jalur sulit dibaca, jadi alangkah baiknya menyaring direktori, misalnya menggunakan opsi baris comman untuk locate.

Apakah ada yang seperti ini? Bahkan jika filter untuk direktori terpisah dari loc?

Paling tidak, orang bisa menggunakan skrip untuk mengulang semua nama file untuk diperiksa - yang mungkin lambat.

Volker Siegel
sumber

Jawaban:

3

Dengan zsh:

print -rl ${(0)^"$(locate -0 ...)"}(N.)

(0)adalah bendera ekspansi parameter yang terbagi pada karakter NUL (seperti yang kita gunakan locate -0), kependekan dari (ps:\0:).

Dengan ^, alih-alih menambahkan (N.)di akhir array, kami menambahkannya ke setiap elemen.

(N.)adalah kualifikasi global, .untuk mencocokkan hanya file biasa, Nuntuk menghapus elemen jika tidak cocok (tidak ada atau bukan file biasa, atau kami tidak dapat memeriksa). Anda juga dapat menggunakan ^/alih-alih .mencocokkan non-direktori bukan hanya file biasa.

print -rlmencetak setiap argumen mentah pada baris terpisah .

Anda dapat menggunakan zshkualifikasi glob apa pun , tetapi perhatikan bahwa pesanan tidak akan berpengaruh, karena kami memperluas satu glob per file di sini, jadi hanya ada satu file yang akan diurutkan untuk masing-masing.

(perhatikan bahwa mungkin gagal jika file terakhir yang dilaporkan locatediakhiri dengan karakter baris baru (ada kesalahan perintah substitusi di semua shell)).

Stéphane Chazelas
sumber
3

Ini hampir tidak sama dengan jawaban lainnya, tetapi mungkin kurang efisien:

locate --regex --basename "xfce4-keyboard-overlay$" | 
        while IFS= read -r f; do [ -f "$f" ] && printf "%s\n" "$f"; done

(dipecah menjadi dua baris untuk dibaca). Di atas akan menangani nama yang berisi spasi. The IFS=tampaknya diperlukan untuk menangani nama dengan mengikuti ruang, dan, tentu saja, -rmemungkinkan Anda menangani backslash.

Pendekatan "mari pipa locatemenjadi sesuatu" mungkin ditakdirkan untuk gagal jika ada nama path yang mengandung baris baru.


Untuk informasi lebih lanjut tentang IFS, baca sh(1)atau bash(1) (dengan mengetik man shatau man bashke sistem * nix, dan / atau membacanya di sini , di sini , di sini , dan / atau di sini ). Kemudian baca Memahami IFS dan Bash: baca baris demi baris, dengan IFS di Stack Exchange (fokus pada jawaban dengan lebih dari 5 suara), dan, jika Anda masih belum cukup, lihat IFS di hasil pencarian Greg Wiki dan IFS Greg di Bash Hackers Wiki (bukan di Stack Exchange).

G-Man Mengatakan 'Reinstate Monica'
sumber
dapatkah Anda menambahkan beberapa info tentang "IFS =" setelah whilepernyataan Anda?
robert
Saya sudah melakukannya.
G-Man Mengatakan 'Reinstate Monica'
backslash masih akan menjadi masalah dengan banyak implementasi gema. Anda harus menggunakan printfdata acak .
Stéphane Chazelas
mungkin ada solusi untuk masalah baris Anda dengan menggunakan "--null" parameter untuk locatedan meningkatkan Anda readseperti yang disarankan di sini transnum.blogspot.ie/2008/11/...
robert
@ StéphaneChazelas: Poin bagus. Tetap.
G-Man Mengatakan 'Reinstate Monica'
2
locate --null --regex --basename "xfce4-keyboard-overlay$" |
  xargs -r0 sh -c 'find "$@" -prune ! -type d' sh
FloHimself
sumber
Sebenarnya, ini bahkan lebih kotor daripada yang terlihat ... tapi inspeksi yang bagus. Mari kita berpura-pura bahwa itu kodesemu, maka ini sangat membantu :)
Volker Siegel
1
@ Volume: Saya setuju bahwa itu buruk: itu akan mendaftar /usr/local/share/xfce4-keyboard-overlay dan setiap subdirektori daripadanya , dalam contoh Anda. Menambahkan -maxdepth 0bantuan.
G-Man Mengatakan 'Reinstate Monica'
Bahkan lebih baik ...: D locate --regex --basename "xfce4-keyboard-overlay$" | xargs -I % sh -c "test -d % && echo %"
FloHimself
1
Menggunakan xargsdengan findadalah ide yang bagus, saya telah mengeditnya untuk membuatnya kuat. Semoga kamu tidak keberatan.
Stéphane Chazelas
1

xargsakan mengulangi perintah untuk setiap baris jika Anda menentukan -L 1atau -iparameter.

Lihat disini

$ locate --regex --basename "xfce4-keyboard-overlay$" | xargs -i bash -c '(test -d "{}" && echo "{}")'

Memang, ini menendang shell baru untuk setiap file, tetapi memang memiliki manfaat baik dan kompak.

EDIT: Saya tidak begitu senang dengan jawaban itu karena itu menendang shell baru untuk setiap file. Ini seharusnya hanya memiliki dua proses:

$ locate --regex --basename "xfce4-keyboard-overlay$" | xargs -i echo 'test -d "{}" && echo "{}"' | bash

Tentu saja akan lebih baik jika kita bisa menghindari menendang juru bahasa sama sekali, tetapi xargstampaknya tidak bisa dipercaya dalam kemampuannya untuk mengaitkan perintah.

robert
sumber
3
Yang baru saja me-reboot mesin saya (ada file yang dipanggil /home/evil/$(reboot)/xfce4-keyboard-overlaydan saya bodoh menjalankannya sebagai root).
Stéphane Chazelas
2
@ StéphaneChazelas +1 untuk keberanian menjalankan "kode acak dari internet" sebagai root;) (scnr)
Volker Siegel
0

Dua sen saya:

while IFS= read i; \
do \
  if [ -f "$i" ]; \
  then \
    echo "$i"; \
  fi; \
done < <(locate --regex --basename "xfce4-keyboard-overlay$")

Ini kurang lebih seperti G-Man melakukannya dikombinasikan dengan proses substitusi.

Tristan Storch
sumber
Sebenarnya, ini kurang lebih seperti cara saya melakukannya, dikombinasikan dengan proses substitusi, minus kemampuan untuk menangani nama file yang mengandung garis miring terbalik atau memiliki ruang putih tertinggal. Juga, perhatikan bahwa judul pertanyaan mengatakan "kecualikan direktori", dan jawaban ini hanya mencakup direktori.
G-Man Mengatakan 'Reinstate Monica'
Maaf. Kesalahanku. Dikoreksi.
Tristan Storch
-1

Bagaimana jika Anda bergabung locatedengan filedan grep...?

$ for f in `locate --regex --basename "xfce4-keyboard-overlay$"`; do file $f; done | grep -vi directory
Petry
sumber
Saya tidak menguji, tetapi saya pikir itu mungkin lambat, karena itu menciptakan proses fileuntuk setiap jalur. Perhatikan bahwa seringkali ada banyak baris hasil untuk ditemukan. Tes saya saat ini sedang mencari "gnome", memberikan sekitar 73000 jalur untuk diuji.
Volker Siegel
2
@ Volume: Lebih buruk dari itu: untuk setiap $fitu adalah file, fileprogram akan membuka file itu dan membaca darinya . Ini sangat mahal ketika semua yang perlu Anda lakukan adalah a stat(). ... ... ... ... Juga, ini akan memberikan hasil yang salah untuk file yang berisi "direktori" dalam nama mereka (seperti "phone_directory"). ... ... ... ... ... (Juga, for f in `…`; do …sintaksis tidak dapat menangani nama yang berisi spasi.)
G-Man Mengatakan 'Reinstate Monica'