Apakah isi direktori yang mengabaikan symlink

17

Saya memiliki direktori di mana saya ingin mendaftar semua konten (file dan sub direktori) tanpa menunjukkan tautan simbolik. Saya menggunakan utilitas GNU di Linux. The lsversi 8.13.

Contoh:

Daftar direktori lengkap:

~/test$ ls -Gg
total 12
drwxrwxr-x 2 4096 Jul  9 10:29 dir1
drwxrwxr-x 2 4096 Jul  9 10:29 dir2
drwxrwxr-x 2 4096 Jul  9 10:29 dir3
-rw-rw-r-- 1    0 Jul  9 10:29 file1
-rw-rw-r-- 1    0 Jul  9 10:29 file2
lrwxrwxrwx 1    5 Jul  9 10:29 link1 -> link1
lrwxrwxrwx 1    5 Jul  9 10:30 link2 -> link2

Apa yang ingin saya dapatkan

~/test$ ls -somthing (or bash hack)
total 12
dir1 dir2 dir3 file1 file2

CATATAN: Motivasi utama saya adalah untuk melakukan grep rekursif (GNU grep 2.10) tanpa mengikuti symlink.

TheMeaningfulEngineer
sumber

Jawaban:

28

Untuk pertanyaan yang disebutkan, Anda dapat menggunakan find:

find . -mindepth 1 ! -type l

akan mencantumkan semua file dan direktori di direktori saat ini atau subdirektori apa pun yang bukan symlink.

mindepth 1hanya untuk melewatkan .entri direktori saat ini. Dagingnya adalah kombinasi dari -type l, yang berarti "adalah hubungan simbolik", dan !, yang berarti meniadakan tes berikut. Dalam kombinasi mereka cocok dengan setiap file yang bukan symlink. Ini mencantumkan semua file dan direktori secara rekursif, tetapi tidak ada symlink.

Jika Anda hanya ingin file biasa (dan bukan direktori):

find . -type f

Untuk memasukkan hanya anak-anak langsung dari direktori ini, dan tidak semua yang lain secara rekursif:

find . -mindepth 1 -maxdepth 1

Anda dapat menggabungkan tes tersebut (dan lainnya) bersama untuk mendapatkan daftar file yang Anda inginkan.

Untuk mengeksekusi tertentu greppada setiap file yang cocok dengan tes yang Anda gunakan, gunakan -exec:

find . -type f -exec grep -H 'some pattern' '{}' +

The '{}'akan diganti dengan file. The +perlu untuk memberitahu findperintah Anda dilakukan. Opsi ini -Hmemaksa grep untuk menampilkan nama file bahkan jika itu kebetulan dijalankan dengan satu file yang cocok.

Michael Homer
sumber
Itu tidak menunjukkan file dalam penyihir pola ditemukan.
TheMeaningfulEngineer
Untuk itulah Anda mencalonkan diri grep(lihat paragraf terakhir).
Michael Homer
Saya tidak bekerja untuk saya. Itu hanya menampilkan pola yang ditemukan tanpa nama file tempat ditemukan.
TheMeaningfulEngineer
2
Gunakan grep -H, jika Anda memilikinya (lihat man grep), atau grep 'pat' '{}' /dev/null ';'untuk menjebaknya dengan berpikir ada beberapa file jika tidak.
Michael Homer
1
+gagal memenuhi persyaratan itu dalam kasus degenerate di mana hanya ada satu file atau satu file tersisa di akhir.
Michael Homer
16

Atau, lebih sederhana:

ls -l | grep -v ^l

Penjelasan

ls -lberarti daftar dalam bentuk panjang . Ketika Anda melakukan ini, string karakter pertama memberikan informasi tentang setiap file. Karakter pertama menunjukkan tipe setiap file. Jika itu symlink, maka a ladalah karakter pertama.

grep -v ^lartinya menyaring ( -v) baris yang dimulai dengan ( ^) an l.

Jonathan Petitcolas
sumber
Ini bekerja dengan baik, tetapi bagaimana cara mendapatkan hanya nama folder? Saya ingin menggunakan ini difor i in
Luka
Bisakah Anda jelaskan kode ini?
abg
Berhati-hatilah karena solusi ini tidak berfungsi jika Anda -lmasuk ls. Jadi ini tidak akan berfungsi seperti yang diharapkan:ls | grep -v ^l
stamster
@Luka Anda tidak mengulangi keluaran find, lihat unix.stackexchange.com/questions/321697
Kusalananda
1
@ abg - Saya menambahkan penjelasan. Semoga ini bisa membantu.
Geoff
7

Dari versi 2.12 dan seterusnya, -ropsi untuk GNU grep tidak menampilkan tautan simbolik kecuali jika Anda menetapkannya dengan tangan:

-r, --recursive

Baca semua file di bawah setiap direktori, secara rekursif, mengikuti tautan simbolik hanya jika ada di baris perintah. Ini sama dengan opsi pengulangan -d.

-R, --dereference-rekursif

Baca semua file di bawah setiap direktori, secara rekursif. Ikuti semua tautan simbolik, tidak seperti -r.

tijagi
sumber
Apa versi grep itu. Saya miliki -R, -r, --recursive Read all files under each directory, recursively; this is equivalent to the -d recurse option.di 2.10
TheMeaningfulEngineer
grep (GNU grep) 2.14
tijagi
5

Di zsh, ini akan mudah berkat kualifikasi global :

grep -- PATTERN **/*(.)

Pola ini **/melintasi subdirektori secara rekursif. Kualifikasi glob .membatasi pencocokan dengan file biasa.

Tanpa zsh, gunakan find(lihat jawaban Michael Horner ). Dan dalam kasus khusus ini, GNU grep dapat melakukan apa yang Anda inginkan (itu persis apa yang grep -rdilakukan) - tetapi hanya sejak versi 2.12, versi sebelumnya memang mengikuti tautan simbolis.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
2

Anda dapat menggunakannya $LS_COLORSuntuk melakukan ini. Jika versi lsdukungan Anda menentukan warna menggunakan variabel itu, Anda dapat menentukan output per jenis file. Itu perilaku bawaan dan sangat dapat dikonfigurasi. Jadi saya membuat beberapa file untuk demo ini seperti:

for f in 9 8 7 6 5 4 3 2 1
    do touch "${f}file" && 
    ln -s ./"${f}file" ./"${f}filelink"
done

Jadi sekarang saya akan lakukan:

LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \
ls -1 --color=always | cat

###OUTPUT###
1file


HERE_THERE_BE_A_LINK>>1filelink@
2file


HERE_THERE_BE_A_LINK>>2filelink@
3file

...
HERE_THERE_BE_A_LINK>>8filelink@
9file
...

Dan null ada di sana juga ...

LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \
ls -1 --color=always | sed -n l
1file$
$
$
\000HERE_THERE_BE_A_LINK>>\0001filelink@$
2file$
$
$
\000HERE_THERE_BE_A_LINK>>\0002filelink@$
3file$
...

Anda dapat menentukan untuk semua atau semua tipe file. Melakukannya hanya untuk satu tipe file mungkin tidak sesuai keinginan Anda karena lstelah memasukkan beberapa nilai kompilasi default untuk lolos dari terminal. Anda akan melakukan jauh lebih baik untuk mengatasi api sebagai satu antarmuka. Berikut adalah sedikit cara sederhana untuk mem-parsing dan menetapkan lingkungan yang dircolorsdikonfigurasi secara default saat ini:

LS_COLORS='rs=:no=//:lc=:rc=:ec=//:'$(
set -- di fi ln mh pi so do bd cd or su sg ca tw ow st ex
for fc do printf %s "$fc=/$fc//:"
done) ls -l --color=always | cat

Outputnya di direktori home saya terlihat seperti ini:

total 884
///-rw-r--r-- 1 mikeserv mikeserv    793 Jul  9 11:23 /fi//1/
//drwxr-xr-x 1 mikeserv mikeserv    574 Jun 24 16:50 /di//Desktop//
//-rw-r--r-- 1 mikeserv mikeserv    166 Jul  4 23:02 /fi//Terminology.log/
//-rw-r--r-- 1 mikeserv mikeserv      0 Jul  6 11:24 /fi//new
file/
//lrwxrwxrwx 1 mikeserv mikeserv     10 Jul 11 04:18 /ln//new
file
link/ -> /fi//./new
file/
//-rwxr-xr-x 1 mikeserv mikeserv    190 Jun 22 11:26 /ex//script.sh/*
//-rw-r--r-- 1 mikeserv mikeserv 433568 Jun 22 17:10 /fi//shot-2014-06-22_17-10-16.jpg/
//-rw-r--r-- 1 mikeserv mikeserv     68 Jun 17 19:59 /fi//target.txt/

Anda dapat menjalankannya cat -Ajuga dan satu-satunya perbedaan yang akan Anda temui adalah Anda akan melihat $baris baru - tidak ada karakter yang tidak diinginkan yang diperkenalkan ls --color=alwaysdengan konfigurasi ini - hanya yang Anda lihat di sini.

ls sisipan terminal defaultnya lolos seperti ini:

${lc}${type_code}${rc}FILENAME${lc}${rs}${rc}

... di mana nilai default untuk $lc (kiri kode) , $rc (kanan kode) , dan $rs (reset) adalah:

 \033 - ESCAPE
 m - END ESCAPE
 0 - reset 

... masing-masing. ${type_code}digunakan untuk menggantikan berbagai fi (file biasa - default tidak disetel) , di (direktori) , ln (tautan) , dan setiap jenis file lain yang saya ketahui. Ada juga $no (normal) yang juga secara default tidak disetel dan yang di sini diwakili oleh //pada awal setiap baris. IFS=:Blok kecil sederhana saya berfungsi hanya dengan menyisipkan nama untuk setiap yang dapat dikonfigurasi juga sebagai nilainya sendiri dan menambahkan satu atau dua garis miring - meskipun \0byte NUL juga akan berlaku.

Secara default lsjuga akan menyisipkan satu $rssegera sebelum output pertama $lc- tetapi ini tidak diwakili secara akurat di sini. Dalam hal ini saya telah menentukan $ec (kode akhir) yang berlaku dalam $rssemua kasus - ketika ditentukan Anda tidak mendapatkan tambahan di $rsantara $nodan ${type_code}seperti yang Anda inginkan - hanya menyajikan segera mengikuti nama file dan sekali pada awal output - seperti yang Anda lihat dalam satu tebasan ekstra di baris pertama.

Ini cuplikan dari saya sendiri $LS_COLORS

printf %s "$LS_COLORS"

rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:\
so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:\
or=40;31;01:su=37;41:sg=30;43:ca=30;41:\
tw=30;42:ow=34;42:st=37;44:ex=01;32:\
*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:...

Dan, sebenarnya, hack shell kecil saya mungkin terlalu rumit - ada antarmuka yang tersedia secara luas untuk menetapkan nilai-nilai ini. Coba dircolors -pdi cli Anda dan info dircolorsuntuk informasi lebih lanjut tentang itu.

Anda dapat membungkus nama file dalam string acak. Anda dapat berkomentar jika Anda mau. Anda dapat menentukan perilaku serupa hanya berdasarkan ekstensi file. Benar-benar tidak banyak yang tidak bisa Anda tentukan dengan cara itu.

Sekarang saya tidak hanya mengada-ada semua ini - saya mempelajarinya setelah menemukan kode sumber secara tidak sengaja.

Dengan konfigurasi khusus ini lsakan memancarkan:

  1. $no - Satu kali per record di awal setiap record

  2. ${type_code}- Sekali segera sebelum setiap nama file untuk menyertakan singkatan dari jenis file dan selalu terjadi pada baris yang sama seperti dan 7 spasi putih dibatasi bidang setelah $no atau segera setelah ->menunjukkan target tautan simbolis.

  3. $ec - Sekali segera sebelum baris pertama dan setelah itu hanya sekali segera mengikuti setiap nama file.

  4. Semua nilai lainnya kosong.

Berikut ini adalah nol-dibatasi ls, dan kali ini saya akan menggunakan cat -A, meskipun, tanpa itu, akan terlihat sama dengan contoh terakhir:

LS_COLORS='rs=:no=\0//:lc=:rc=:ec=\0//:'$(
set -- di fi ln mh pi so do bd cd or su sg ca tw ow st ex
for fc do printf %s "$fc=/$fc//\0:"
done) ls -l --color=always | cat -A

total 884$
^@//^@//-rw-r--r-- 1 mikeserv mikeserv    793 Jul  9 11:23 /fi//^@1^@//$
^@//drwxr-xr-x 1 mikeserv mikeserv    574 Jun 24 16:50 /di//^@Desktop^@///$
^@//-rw-r--r-- 1 mikeserv mikeserv    166 Jul  4 23:02 /fi//^@Terminology.log^@//$
^@//-rw-r--r-- 1 mikeserv mikeserv      0 Jul  6 11:24 /fi//^@new$
file^@//$
^@//lrwxrwxrwx 1 mikeserv mikeserv     10 Jul 11 04:18 /ln//^@new$
file$
link^@// -> /fi//^@./new$
file^@//$
^@//-rwxr-xr-x 1 mikeserv mikeserv    190 Jun 22 11:26 /ex//^@script.sh^@//*$
^@//-rw-r--r-- 1 mikeserv mikeserv 433568 Jun 22 17:10 /fi//^@shot-2014-06-22_17-10-16.jpg^@//$
^@//-rw-r--r-- 1 mikeserv mikeserv     68 Jun 17 19:59 /fi//^@target.txt^@//$

Dan untuk menghapus semua tautan simbolis dari -ldaftar ong seperti ini dengan andal , Anda dapat melakukan perubahan sederhana:

LS_COLORS='rs=:no=//:lc=:rc=:ec=/ :'$(
set -- di fi mh pi so do bd cd or su sg ca tw ow st ex
for fc do printf %s "$fc=$fc/:"
done)ln=///: ls -l --color=always | sed ':ln
\|///|{N;\|\n//|!bln};s|.*//||'

Hasil saya setelah menjalankan yang terlihat seperti ...

total 884
-rw-r--r-- 1 mikeserv mikeserv    793 Jul  9 11:23 fi/1/ 
drwxr-xr-x 1 mikeserv mikeserv    574 Jun 24 16:50 di/Desktop/ /
-rw-r--r-- 1 mikeserv mikeserv    166 Jul  4 23:02 fi/Terminology.log/ 
-rw-r--r-- 1 mikeserv mikeserv      0 Jul  6 11:24 fi/new
file/ 
-rwxr-xr-x 1 mikeserv mikeserv    190 Jun 22 11:26 ex/script.sh/ *
-rw-r--r-- 1 mikeserv mikeserv 433568 Jun 22 17:10 fi/shot-2014-06-22_17-10-16.jpg/ 
-rw-r--r-- 1 mikeserv mikeserv     68 Jun 17 19:59 fi/target.txt/ 

Menggunakan beberapa perintah seperti yang saya lakukan di atas:

LSCOLORS=...$(...)fc1=///:fc2=///: ls ... | sed ...

... (di mana fc1dan fc2merupakan tipe file yang terdaftar setelah set --dalam subkulit) harus berfungsi untuk menghapus semua kombinasi tipe file yang Anda inginkan dari lsoutput terlepas dari karakter apa pun yang mungkin berisi nama file.

mikeserv
sumber
Heh, bagus, +1 untuk orisinalitas. Tampaknya tersedak pada direktori, mereka dicetak dengan kode warna ANSI kacau. Lagi pula, bisakah Anda menambahkan penjelasan tentang cara kerjanya? Mungkin menjelaskan apa lc, nc, dll?
terdon
@terdon - Mereka hanya dicetak dengan pelarian ANSI yang telah ditetapkan di lingkungan Anda dan atau dikompilasi sebagai nilai default untuk lsitu di=. Anda harus menambahkan di=:var untuk membatalkannya - dan untuk semua yang lain. Itu yang saya maksud dengan api - Anda alamat setiap jenis file tetapi Anda harus alamat antarmuka yang sama dalam semua kasus. Jadi pengaturan lnsatu arah dan mengabaikan distandar tidak akan berhasil. Ada banyak standar yang dikompilasi juga ... Hmm. Meminta Anda tentang lcdan ncdan hal-hal berlaku - benar - saya sudah melakukannya hari lain sekalipun. Saya akan menghubungkannya, saya pikir.
mikeserv
Aduh, jadi saya harus menentukan semua jenis file yang mungkin selalu atau memeriksa apa yang ada sebelumnya? Ok terima kasih.
terdon
@terdon - lihat edit saya sekarang - itu melakukan semuanya dalam beberapa baris shell, menentukan setiap jenis dengan nama jenisnya dan andal membatasi setiap nama file. Tidak ada yang tidak dapat dicetak diperkenalkan juga. Dan ada api untuk itu - itu dircolors. Bunyinya file konfigurasi dan menampilkan evalnilai var yang mudah digunakan. Ini cukup mudah - tetapi begitu juga beberapa baris di sana. Lagi pula, jika Anda ingin menggunakan dircolorsAnda hanya menyimpan dua file yang berbeda dan memohon seperti itu. Juga, saya menunjukkan kepada Anda suatu hari bagaimana melakukan keduanya sekaligus dengan \0pembatas NUL sebagai bagian dari setiap urutan pelarian.
mikeserv
@terdon - Saya mencoba memperjelas betapa mudahnya mengatur semua nilai yang mungkin dikompilasi - saya pikir saya punya semuanya, mungkin terlalu banyak ... LS_COLORS='rs=:no=\0//:lc=:rc=:ec=\0//:'$( set -- di fi ln mh pi so do bd cd or su sg ca tw ow st ex; for fc do printf %s "$fc=/$fc//\0:"; done) ls -l --color=always | cat -A
mikeserv
0

Jawaban di atas mengarah pada yang berikut:

 ls -1F | grep -i "/"

Anehnya meninggalkan 1 dari perintah ls masih memberikan daftar.

woodsjay
sumber
1
Mengapa grep -i? Bukannya /bisa menjadi huruf besar atau huruf kecil. lsdefault ke format noe-kolom ketika output bukan terminal (itu pipa di sini).
Kusalananda
-2

Coba yang ini:

ls | grep -v " -> "
csny
sumber
2
Itu akan gagal karena berbagai alasan, misalnya pada file bernama a -> b. Ini juga akan gagal pada nama file dengan baris baru dan keanehan lainnya. Baca ini untuk alasan mengapa mem-parsing adalah ide yang buruk.
terdon
@terdon Ada alasan yang lebih mendasar mengapa ini akan gagal. Entah lsalias ls -l, dan ini mengembalikan kolom sebelum nama file, atau tidak, dan ini tidak melakukan sesuatu yang istimewa tentang tautan simbolik.
Gilles 'SO- berhenti bersikap jahat'
@terdon - lsmem - parsing sendiri. kita semua menguraikan lssetiap kali kita mencetak ke terminal - ada api . Saya mengirim jawaban yang menunjukkan sesuatu seperti itu di sini - tetapi Anda dapat melakukan lebih banyak lagi. Saya menunjukkan kepada Anda beberapa hari yang lalu - Anda memiliki nama file nol-dibatasi sendiri.
mikeserv
@ mikeserv aku tahu, aku tahu. Namun, menjelaskan cara melakukan ini dengan aman membutuhkan beberapa ratus baris seperti yang telah Anda tunjukkan. Seperti berdiri, jawaban ini menderita semua bahaya parsing dan OP jelas tidak menyadarinya.
terdon
@terdon - lstidak akan memakan anak-anak Anda atau apa pun - itu hanya sebuah program. Jika Anda tidak menjelaskannya maka akan memakan waktu beberapa ratus untuk menjelaskan hal itu dan beberapa ratus lebih untuk menjelaskan mengapa beberapa ratus baris Anda hanya terkait dengan salah. Dan selain itu - ini hanya dua baris:LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \ ls -1 --color=always | cat
mikeserv
-3

Coba perintah ini: ls -p | grep -v @

Thushi
sumber
Tidak, daftarkan tautannya.
TheMeaningfulEngineer
@Lan: k coba inifind -type f
Thushi
1
-1 karena: i) Yang -pbaru saja menambahkan garis miring ke direktori, Anda mencari -Fii) findalternatif Anda tidak akan mendaftar direktori dan iii) Silakan menguji jawaban Anda sebelum memposting.
terdon