Kapan string kosong menunjukkan direktori saat ini?

8

Dalam skrip yang saya gunakan finduntuk mengumpulkan beberapa file di direktori saat ini, seperti pada

$ find . -name "*.h"
./foo.h

Sekarang saya ingin hanya output foo.h, tanpa ./awalan. Saya pikir string kosong ""menunjukkan direktori saat ini dalam perintah shell. Tapi ini memberi:

$ find "" -name "*.h"
find: ftsopen: No such file or directory

Jadi saya salah. Sekarang pertanyaan saya adalah kapan / bagaimana / di mana / .. apakah sebuah "string kosong (?)" Menunjukkan perintah saat ini dalam perintah yang mengharapkan nama file atau nama path? Apakah ada penjelasan yang rapi dan mencerahkan?

Pertanyaan sampingannya adalah apakah temuan nitpicking di atas dapat diselesaikan dengan mudah, tanpa manipulasi string ala ${parameter#word}atau cutatau sed?

phs
sumber
1
findmemiliki parameter -printf untuk memanipulasi bagaimana hasil ditampilkan. find . -name '*.h' -printf '%P\n'akan menghapus ./awalan. Lihat apa find . -name '*.h' -print.
Valentin Bajrami
Terima kasih! Sayang sekali versi temuan saya tidak mendukung -printfopsi. FWIW, stringspada temuan saya memberi @(#)PROGRAM:find PROJECT:shell_cmds-175?!?
phs
5
@ Phs, itulah hal yang membuatnya penting bahwa Anda selalu menyebutkan OS Anda.
terdon

Jawaban:

11

Sebuah waktu yang lama lalu (di edisi ke-7 , 32V , 4.2BSD , 4.3BSD ), pada tingkat sistem-panggilan nol-panjang pathname dilambangkan direktori kerja saat ini (saat digunakan untuk pencarian; itu dianulir ketika mencoba untuk membuat atau menghapus suatu file atau direktori). Dalam Sistem III , itu adalah kesalahan untuk menggunakan nama path panjang nol dalam semua keadaan, dan standar POSIX mengatakan ini tentang resolusi pathname:

Pathname nol tidak akan berhasil diselesaikan.

Tandai Plotnick
sumber
3
String kosong dalam $ PATH, $ MANPATH, $ LD_LIBRARY_PATH (dan yang lainnya tetapi tidak harus semua $ * PATH) menunjukkan direktori saat ini. dir/sebagian besar sama dengandir/.
Stéphane Chazelas
4

Kamu bisa menggunakan:

find * -name "*.h"

Perhatikan bahwa file dalam direktori saat ini yang namanya dimulai dengan .akan dihilangkan, dan file yang namanya dimulai dengan -akan ditafsirkan sebagai opsi oleh finddan menyebabkan kekacauan, jadi ini bukan setara dengan umum find . ….

Tidak adanya string yang diakhiri dengan " /" sebagai bagian dari nama file menyiratkan direktori saat ini, tetapi itu tidak berarti direktori saat ini dilambangkan dengan string kosong (yang bisa dibilang tidak sama dengan tidak adanya string, meskipun mungkin terlihat sama saat dicetak).

Anthon
sumber
4

Secara umum, string kosong tidak menunjukkan direktori saat ini, baik untuk perintah shell maupun panggilan sistem. Itu terjadi pada beberapa sistem lama, tetapi tidak pada sistem yang sesuai dengan POSIX .

Kadang-kadang Anda akan menemukan program yang menggunakan direktori saat ini ketika Anda melewatkan string kosong dan program mengharapkan nama direktori. Ini kadang-kadang disengaja, dan kadang-kadang efek samping dari prepending path absolut direktori saat ini ketika string yang diberikan tidak dimulai dengan slash.

Hal terbaik untuk Anda adalah meninggalkan ./. Tidak ada salahnya.

Jika daftar file untuk

find . … | sed 's!^\./!!'

Perhatikan bahwa ini mangles beberapa nama file yang mengandung baris baru. Ini biasanya bukan masalah untuk konsumsi manusia, dan output dari findtidak cocok untuk konsumsi program karena ambigu. Jika Anda menggunakan -print0, yang cocok untuk konsumsi program, Anda mungkin tidak peduli dengan ./awalannya.

Anda dapat menggunakan find * …sebagai gantinya find . …, tetapi perhatikan bahwa find *memiliki sejumlah cacat yang membuatnya tidak cocok secara umum:

  • . dihilangkan.
  • Semua file titik (file yang namanya dimulai dengan .atau ..`) dihilangkan.
  • Jika ada nama file di direktori saat ini yang namanya dimulai dengan -(atau file bernama !atau (...), itu akan ditafsirkan sebagai opsi atau predikat oleh find.

Poin pertama tidak masalah jika filter Anda mengecualikan direktori saat ini. Untuk poin kedua, Anda dapat menggunakan pola ..?* .[!.]* *untuk mencocokkan semua file di direktori saat ini, tetapi Anda harus memeriksa apakah setiap pola cocok dengan setidaknya satu file dan menghilangkannya jika tidak. Ini mungkin tetapi sangat rumit. Poin terakhir adalah stopper. Jadi find *mungkin cocok untuk penggunaan baris perintah quickie, tetapi jangan menggunakannya dalam skrip.

Pendekatan alternatif adalah dengan menggunakan fasilitas globbing rekursif shell, misalnya

printf '%s\n' **/*.h

Ini perlu diaktifkan oleh shopt -s globstardi bash dan oleh set -o globstardi ksh93, dan tidak ada di shell POSIX dasar seperti dasbor. File dot tidak akan dilalui secara default; untuk memasukkannya, pertama-tama buat globbing jangan abaikan file dot dengan shopt -s dotglobdi bash atau FIGNORE='@(.|..)'di ksh93. Juga, jika tidak ada kecocokan, maka perintah ini mencetak pola; jalankan shopt -s nullglobbash untuk mencetak baris kosong, dan gunakan pola ~(N)**/*.hdalam ksh.

Di zsh, globbing rekursif diaktifkan secara default. Gunakan kualifikasi glob Duntuk memasukkan file titik dan Nuntuk mencetak baris kosong jika tidak ada yang cocok (secara default, zsh melempar kesalahan jika suatu pola tidak cocok dengan file apa pun). Anda dapat menggunakan printfseperti di atas atau

print -rl -- **/*.h(DN)
Gilles 'SANGAT berhenti menjadi jahat'
sumber
Terima kasih untuk semua tips ini. Sebagian besar cacat dengan find *saya baru, dan beberapa menakutkan !!
phs
2

Saya tidak dapat memikirkan contoh di mana string kosong menunjukkan direktori saat ini .. Anda mungkin berpikir tentang doa ls, tetapi itu karena lsmenganggap direktori saat ini jika tidak ada parameter yang diberikan, dan faktanya itu tidak akan mengambil string kosong:

ulmi@silberfisch:~$ ls ""
ls: cannot access : No such file or directory
Ulrich Schwarz
sumber
3
Salah satu contoh di mana string kosong menunjukkan direktori saat ini adalah sebagai PATHkomponen.
hvd
0

Ada berbagai trik yang dapat Anda gunakan untuk mendapatkan hasil find tanpa yang memimpin ./:

  1. Gunakan -printfopsi temukan dan katakan untuk hanya mencetak %f. Lihat man find:

    % f Nama file dengan direktori utama yang dihapus (hanya elemen terakhir).

    Sebagai contoh:

    find . -name "*.h" -printf "%f\n"
    
  2. Parsing hasilnya:

    find . -name "*.h" | sed 's#^./##'
    
  3. @ Trik Anthon

    find * -name "*.h"
    

Adapun pertanyaan Anda yang lain, string kosong tidak pernah menunjukkan direktori saat ini. Hanya saja berbagai program menggunakan direktori saat ini sebagai default sehingga ketika Anda menjalankannya tanpa argumen, dijalankan di direktori saat ini.

terdon
sumber
Itu mengasumsikan GNU find. Atau gunakan %Puntuk hanya mengupas./
Stéphane Chazelas
1
find * -name '*.h'akan menemukan foo/.bar.h, tetapi tidak .bar.hdi direktori saat ini.
Stéphane Chazelas
0

Jika file berada di direktori saat ini Anda bisa menggunakan ls:

$ ls *.sh

Jika Anda ingin file dalam subdirektori juga dan hanya perlu nama file Anda bisa melakukan sesuatu seperti:

$ find . -name '*.h' -exec basename {} \;

Ada perintah yang akan mendapatkan tidak adanya string sebagai direktori saat ini, tetapi pada dasarnya karena mereka akan mendapatkan direktori saat ini sebagai default jika tidak ada yang dimasukkan. The .selalu menyatakan dir saat ini (dan ..direktori induk)

Karl
sumber
OP hanya ingin ./ dihapus, sementara findsolusi Anda menghapus semua komponen direktori
artm