mengapa ls -d juga membuat daftar file, dan di mana itu didokumentasikan?

48
  • ketika menentukan ls --directory a*itu harus mendaftar hanya direktori yang dimulai dengana*
  • TAPI daftar file DAN direktori dimulai dengan a

Pertanyaan :

  • di mana saya dapat menemukan beberapa dokumentasi tentang ini, selain mandan di infomana saya pikir saya benar-benar melihat?
  • apakah ini hanya berfungsi dalam BASH?
erch
sumber
1
Lihat juga pertanyaan serupa lainnya .
Stéphane Chazelas
5
echo a*juga akan mencantumkan file yang dimulai dengan a(jika ada). Untuk membuatnya lebih jelas bahwa itu tidak lsmelakukan ini tetapi bash.
ash108

Jawaban:

89

The a*dan *a*sintaks dilaksanakan oleh shell, bukan oleh lsperintah.

Saat Anda mengetik

ls a*

pada prompt shell Anda, shell memperluas a*ke daftar semua file yang ada di direktori saat ini yang namanya dimulai dengan a. Misalnya, mungkin memperluas a*ke urutan a1 a2 a3, dan meneruskannya sebagai argumen ls. The lsperintah itu sendiri tidak pernah melihat *karakter; hanya melihat tiga argumen a1, a2dan a3.

Untuk keperluan ekspansi wildcard, "file" merujuk ke semua entitas dalam direktori saat ini. Misalnya, a1mungkin file biasa, a2mungkin direktori, dan a3mungkin symlink. Mereka semua memiliki entri direktori, dan ekspansi wildcard shell tidak peduli seperti apa entitas entri itu merujuk.

Praktis semua shell yang mungkin Anda temui (bash, sh, ksh, zsh, csh, tcsh, ...) menerapkan wildcard. Detailnya dapat bervariasi, tetapi sintaks dasar *pencocokan karakter nol atau lebih dan ?pencocokan karakter tunggal cukup konsisten.

Khususnya untuk bash, ini didokumentasikan di bagian "Perluasan nama file" dari manual bash; jalankan info bashdan cari "Perluasan nama file", atau lihat di sini .

Fakta bahwa ini dilakukan oleh shell, dan bukan oleh perintah individu, memiliki beberapa konsekuensi yang menarik (dan kadang mengejutkan). Hal terbaik tentang itu adalah penanganan wildcard konsisten untuk (hampir) semua perintah; jika shell tidak melakukan ini, pasti beberapa perintah tidak akan mengganggu, dan yang lain akan melakukannya dengan cara yang agak berbeda yang menurut penulis adalah "lebih baik". (Saya pikir shell perintah Windows memiliki masalah ini, tapi saya tidak cukup akrab dengannya untuk berkomentar lebih lanjut.)

Di sisi lain, sulit untuk menulis perintah untuk mengganti nama banyak file. Jika Anda menulis:

mv *.log *.log.bak

mungkin akan gagal, karena *.log.bakdiperluas berdasarkan file yang sudah ada di direktori saat ini. Ada perintah yang melakukan hal semacam ini, tetapi mereka harus menggunakan sintaks mereka sendiri untuk menentukan bagaimana file akan diganti namanya. Beberapa perintah (seperti find) dapat melakukan ekspansi wildcard mereka sendiri; Anda harus mengutip argumen untuk menekan ekspansi shell:

find . -name '*.txt' -print

Ekspansi wildcard shell seluruhnya didasarkan pada sintaks dari argumen command-line dan set file yang ada. Itu tidak bisa dipengaruhi oleh arti perintah. Misalnya, jika Anda ingin memindahkan semua .logfile ke direktori induk, Anda dapat mengetik:

mv *.log ..

Jika Anda lupa ..:

mv *.log

dan kebetulan ada dua .logfile di direktori saat ini, itu akan diperluas ke:

mv one.log two.log

yang akan mengganti nama one.logdan clobber two.log.

EDIT : Dan setelah 52 upvotes, menerima, dan lencana Guru, mungkin saya harus benar-benar menjawab pertanyaan dalam judul.

The -datau --directorypilihan untuk lstidak kirim ke daftar hanya direktori. Ini memberitahu untuk daftar direktori seperti diri mereka sendiri, bukan isinya. Jika Anda memberikan nama direktori sebagai argumen ls, secara default ia akan mencantumkan isi direktori, karena biasanya itulah yang Anda minati. -dOpsi ini memberitahukannya untuk mencantumkan hanya direktori itu sendiri. Ini bisa sangat berguna ketika dikombinasikan dengan wildcard. Jika Anda mengetik:

ls -l a*

lsakan memberi Anda daftar panjang setiap file yang namanya dimulai a, dan dari isi setiap direktori yang namanya dimulai a. Jika Anda hanya ingin daftar file dan direktori, satu baris untuk masing-masing, Anda dapat menggunakan:

ls -ld a*

yang setara dengan:

ls -l -d a*

Ingat lagi bahwa lsperintah tidak pernah melihat *karakter.

Adapun tempat ini didokumentasikan, man lsakan menunjukkan kepada Anda dokumentasi untuk lsperintah pada hampir semua sistem seperti Unix. Pada kebanyakan sistem berbasis Linux, lsperintahnya adalah bagian dari paket GNU coreutils; jika Anda memiliki infoperintah, salah satu info lsatau info coreutils lsharus memberi Anda dokumentasi yang lebih definitif dan komprehensif. Sistem lain, seperti MacOS, dapat menggunakan versi berbeda dari lsperintah, dan mungkin tidak memiliki infoperintah; untuk sistem tersebut, gunakan man ls. Dan ls --helpakan menampilkan pesan penggunaan yang relatif singkat (117 baris di sistem saya) jika Anda menggunakan implementasi GNU coreutils.

Dan ya, bahkan para ahli perlu membaca dokumentasi sekarang dan nanti. Lihat juga lelucon klasik ini .

Keith Thompson
sumber
8
@ Thomas: a*memperluas ke daftar semua file di direktori saat ini yang namanya dimulai dengan a.
Keith Thompson
2
@ Thomas: Atau di direktori apa pun yang Anda tentukan:ls subdir/a*
Keith Thompson
1
Untuk benar-benar melihat perintah dieksekusi setelah ekspansi shell, echoitu. Jadi jika Anda ingin tahu apa yang terjadi ketika Anda melakukannya ls a*, pertama-tama jalankanecho ls a*
Carlos Campderró
1
atau hanya echo a*... shell melakukan ekspansi glob
Useless
2
@sendmoreinfo: Itu tergantung pada shell dan pengaturannya. Dalam csh dan tcsh, ekspansi glob yang gagal adalah kesalahan. Di bash, shopt -s failglobmenyebabkan perilaku yang sama. Pengaturan nullglobtetapi tidak failglobmenyebabkan, misalnya, *nosuchfile*untuk memperluas ke string kosong.
Keith Thompson
27

Lihat jawaban Keith Thompson; tetapi untuk menjelaskan mengapa ls --directory a*memperlihatkan file dan direktori: --directoryOpsi tidak menekan file non-direktori. Alih-alih, ia mendaftar direktori seperti itu, sementara itu jika tidak akan daftar kontennya. Contoh:

$ mkdir foo
$ touch foo/bar
$ ls foo
bar
$ ls --directory foo
foo
chirlu
sumber
3
contoh itu lebih menarik dengan -Fopsi
glenn jackman
6

Untuk menjadi sangat eksplisit, ini didokumentasikan dalam halaman manual ls (1) :

-d, --directory daftar direktori bukan isi, dan jangan dereferensi tautan simbolik

agar adil, "entri bukan konten" mungkin bisa lebih jelas:

Jika FILE adalah direktori, perlihatkan entri direktori itu sendiri dan bukan daftar isi direktori itu. Jika FILE adalah tautan simbolis, perlihatkan entri tautan itu sendiri alih-alih file yang ditunjuk oleh tautan tersebut.

msw
sumber
Untuk menjadi bertele-tele, sementara opsi -d dapat dijelaskan (jika jelas) di halaman manual, ekspansi argumen dan wildcarding tidak didokumentasikan di halaman manual karena itu dilakukan oleh shell. Jika Anda mematikan ekspansi nama file di shell Anda, "ls a *" hanya akan menampilkan file yang secara harfiah bernama 'a *'.
Johnny
Menjadi bertele-tele, sementara ekspansi shell dijawab oleh responden lain, tidak satupun dari mereka membahas sedikit penjelasan dan bagaimana mungkin salah membaca. Jika Anda ingin saya mengulangi apa yang sudah dikatakan dengan baik, harap lebih langsung.
msw 8'13
4

Globbing

Seperti yang telah dijelaskan, perluasan *(dan ekspansi serupa) disebut globbing di jargon Unix, dan biasanya merupakan fitur dari prosesor perintah (dikenal sebagai "shell" dalam bahasa Unix). Jadi globbing dapat digunakan di banyak tempat lain juga; ketik man 7 globdi shell dari distribusi Linux klasik (atau lihat ini ) untuk informasi lebih lanjut tentang globbing.

Pada awal Unix, globsebenarnya dilaksanakan oleh program terpisah yang disebut /etc/glob(lihat halaman 10 manual UNIX lama ini untuk dokumentasi untuk itu). Saat ini, ini adalah rutin kode yang disediakan oleh pustaka kode, dan biasanya digunakan oleh shell. Sumber : Wikipedia .

Direktori

Mengapa ls -ddaftar file serta direktori ...

The lsman page menjelaskan mengapa hal ini terjadi, tetapi dengan penjelasan yang sangat singkat. Jadi, inilah upaya penjelasan yang diperluas:

Secara default lsdaftar isi direktori ketika diberi nama mereka, dan akan melakukan hal yang sama untuk symlink ke direktori. The -dpilihan berarti, "ketika diberi nama (s) dari direktori (ies)" (yang juga dapat diberikan secara implisit oleh globbing) , hanya menunjukkan _name_s dari direktori (ies), tetapi tidak isinya. Demikian pula, ketika disediakan dengan (secara eksplisit atau implisit ) nama symlink ke direktori , tunjukkan nama file symlink, bukan isi direktori yang dirujuk.

The -dpilihan tidak ada hubungannya, sejauh yang saya tahu, dengan yang item yang tercantum; ini dapat dilakukan (sumber: disini ) dengan find, seperti: find . -maxdepth 1 -type d. Saya tidak yakin apakah ada cara yang baik untuk melakukannya hanya dengan GNU ls. Berikut adalah beberapa contoh cara menggunakan findperintah.

Jadi untuk meringkas: Secara default lsmenunjukkan isi direktori ketika diberi nama path mereka. -dmengubah perilaku ini, hanya menampilkan nama direktori itu sendiri, seperti yang akan dilakukan untuk file standar.

Abbafei
sumber
3

Dokumentasi tidak mengatakan itu hanya daftar entri direktori tetapi ketika lsmenerima nama direktori itu daftar entri dir alih-alih isinya. Cara terbaik untuk memahami adalah dengan contoh:

> {ice} ~ :10:47 % ls -l / 
total 97
drwxr-xr-x   2 root root  4096 May  3 00:27 bin
drwxr-xr-x   4 root root  1024 May  3 14:17 boot
drwxr-xr-x   2 root root  4096 Apr 29 13:44 cdrom
drwxr-xr-x  18 root root  4420 May  9 09:58 dev
...
lrwxrwxrwx   1 root root    33 May  3 14:16 vmlinuz -> boot/vmlinuz-3.9.0-030900-generic
lrwxrwxrwx   1 root root    29 May  3 11:07 vmlinuz.old -> boot/vmlinuz-3.8.0-19-generic
> {ice} ~ :10:47 % ls -ld /
drwxr-xr-x 25 root root 4096 May  3 14:16 /
defhlt
sumber
2

Dokumentasi adalah bagian dari shell . Secara khusus shell melakukan berbagai ekspansi dan penggantian dalam urutan tertentu sebelum menjalankan perintah.

Urutan ekspansi kata untuk shell yang kompatibel dengan Bourne adalah

  1. Ekspansi Tilde
  2. Ekspansi Parameter
  3. Substitusi Perintah
  4. Ekspansi Aritmatika
  5. Bidang Memecah
  6. Perluasan Pathname
  7. Penghapusan Kutipan

Jadi, lsperintahnya bahkan tidak melihat *karakter, argumen sudah diperluas dengan semua file yang cocok dengan a*pola glob. Ini tidak seperti pada Windows, di mana setiap perintah yang membutuhkan globbing nama file harus mengimplementasikan fitur ini sendiri.

Jens
sumber
1

Kata kunci yang Anda butuhkan ( man bash) adalah "Perluasan Pathname".

Hauke ​​Laging
sumber
3
lebih seperti "Perluasan Pathname" atau "Pembuatan Nama File". Terkait dengan "pencocokan pola", tetapi tidak sama.
Stéphane Chazelas
@StephaneChazelas Memang, tetapi "pencocokan pola" adalah subbagian dari "ekspansi pathname" di halaman manual sedangkan "Filename Generation" tidak muncul sama sekali (hanya di dokumen info).
Hauke ​​Laging