'ls -1': cara membuat daftar nama file tanpa ekstensi

24

ls -1 daftar elemen saya seperti ini:

foo.png
bar.png
foobar.png
...

Saya ingin terdaftar tanpa .pngseperti:

foo
bar
foobar
...

(dir hanya berisi .pngfile)

Adakah yang bisa memberi tahu saya cara menggunakannya grepdalam kasus ini?

Tujuan: Saya memiliki file teks di mana semua nama terdaftar tanpa ekstensi. Saya ingin membuat skrip yang membandingkan file teks dengan folder untuk melihat file mana yang hilang.

Colin
sumber
36
Anda ingin berhati-hati dengan permintaan seperti ini. Linux tidak memiliki ekstensi nama file. Linux memiliki nama file yang mungkin atau mungkin tidak termasuk .di dalamnya. Meskipun konvensi mengatakan untuk memberi nama file Anda .pngpada akhirnya, tidak ada alasan mengapa saya tidak dapat memiliki file png bernama foo.zipatau my.picture.20160518atau hanya mypic.
nyanyian
2
@ Saya tahu, tapi elemen saya di folder itu semuanya dinamai dengan .png di akhir.
Colin
14
Apa itu "ekstensi"? Itu bukan bagian dari penamaan file Unix; itu akumulasi dari VMS / NT / Windows apa pun. Dan kalian, anak-anak, turun dari halaman saya juga. :)
mpez0
28
Jangan melebih-lebihkan ini. OS memperlakukan ekstensi hanya sebagai bagian dari nama file, tetapi banyak program unix memperhatikannya, dari kompiler hingga GUI. Konsepnya tentu tidak asing dengan unix.
alexis
1
Biasanya disarankan untuk tidak mem-parsing output darils dan untuk mem- pipe output dari lsdan find, terutama karena kemungkinan untuk menimbulkan newline, `tab char dalam nama file. Jika nama file The new art of working on .png\NEWLINE files and other formatsbanyak dari solusi yang diusulkan akan menimbulkan masalah.
Hastur

Jawaban:

41

Anda hanya perlu shell untuk pekerjaan ini.

POSIXly:

for f in *.png; do
    printf '%s\n' "${f%.png}"
done

Dengan zsh:

print -rl -- *.png(:r)
cuonglm
sumber
4
Tidak perlu printf; echo ${f%.png}akan cukup.
David Conrad
11
@Conrad: menggunakan gema tidak akan berfungsi dengan baik dalam beberapa kasus, jika nama file dimulai dengan tanda hubung atau berisi urutan yang diloloskan.
cuonglm
3
@DavidConrad: Lihat juga unix.stackexchange.com/a/65819/38906
cuonglm
35
ls -1 | sed -e 's/\.png$//'

The sedMenghapus perintah (yaitu, menggantikan dengan string kosong) string .pngyang ditemukan pada akhir dari nama file.

Ini .lolos \.sehingga ditafsirkan olehsed sebagai .karakter literal daripada regexp .(yang berarti cocok dengan karakter apa pun). Ini $adalah jangkar akhir baris, sehingga tidak cocok .pngdi tengah nama file.

cas
sumber
4
Saya pikir OP ingin setiap ekstensi dilucuti, tapi mungkin hanya "terakhir". Jadi, mungkin ubah jawaban Anda yang baik dengan:sed 's/\.[^.]*$//'
Otheus
1
ya, regexp itu akan berfungsi dalam kasus itu ... tetapi jika OP menginginkan itu, mereka harus mengatakannya alih-alih secara spesifik mengatakan mereka "ingin terdaftar tanpa .png"
cas
4
Tidak -1perlu, menjadi standar di sini.
jlliagre
3
@ jlliagre Saya setuju dengan cas bahwa -1harus ditentukan. Ini hanya default ketika pipa dihidupkan, yang merupakan kejutan tersembunyi bagi sebagian orang. Jadi, membuatnya eksplisit membantu pemahaman. Saya juga melakukan ini di skrip saya jadi saya tahu apa jenis output yang saya harapkan.
Otheus
1
Peringatan Dalam kasus nama file dengan kunci ( .png) sebelum karakter baris baru Anda akan menghapus bahkan itu .pngdan tidak hanya yang terakhir. Lebih baik menghindari pipa dan mem-parsing output ls, itu menyimpan kejutan yang sering tersembunyi ... (beberapa kata dan referensi lebih banyak dalam jawabannya).
Hastur
16

Jika Anda hanya ingin menggunakan bash:

for i in *; do echo "${i%.png}"; done

Anda harus meraih grepketika mencoba menemukan kecocokan, bukan untuk menghapus / menggantikannyased lebih tepat:

find . -maxdepth 1 -name "*.png"  | sed 's/\.png$//'

Setelah Anda memutuskan Anda perlu membuat beberapa subdirektori untuk memesan file-file PNG Anda, Anda dapat dengan mudah mengubahnya menjadi:

find . -name "*.png"  | sed 's/\.png$//'
Anthon
sumber
ls -1 | sed 's / .png //' sangat bagus. Terima kasih!
Colin
Solusi yang find disalurkan ke sedsolusi dapat menyajikan beberapa masalah jika Anda menemukan file dengan kunci ( .png) sebagai bagian dari nama dan tepat sebelum karakter baris baru. Lebih baik untuk menghindari pipa dan mengurai output findatau ls, itu menyimpan kejutan yang sering tersembunyi ... (beberapa kata dan referensi lebih dalam jawabannya).
Hastur
Mungkin ganti finddengan sesuatu seperti echopada contoh terakhir. Tidak jelas tujuan apa yang findberfungsi di sana dan hasilnya tergantung pada struktur direktori (yaitu jika Anda memiliki direktori files.png)
@ BroSlow Diperbarui pada sesuatu yang lebih masuk akal.
Anthon
13

Saya akan pergi basename(dengan asumsi implementasi GNU):

basename --suffix=.png -- *.png
hennr
sumber
Perhatikan bahwa jika Anda ingin menggunakannya dalam sebuah pipa, Anda mungkin merasa terbantu untuk menggunakan opsi GNU -z(atau --zero) untuk menghasilkan output yang dipisahkan-NUL (bukan dipisahkan-baris).
Toby Speight
11

Jawaban lain yang sangat mirip (saya terkejut varian khusus ini belum muncul) adalah:

ls | sed -n 's/\.png$//p'
  • Anda tidak perlu -1opsi untuk ls, karenals mengasumsikan bahwa jika output standar bukan terminal (ini adalah pipa, dalam hal ini).
  • yang -npilihan untuksed sarana 'tidak mencetak baris dengan default'
  • yang /ppilihan pada akhir cara substitusi '... dan mencetak baris ini jika substitusi dibuat'.

Efek bersihnya adalah mencetak hanya garis-garis yang berakhir .pngdengan .pngpenghapusan. Artinya, ini juga melayani generalisasi sedikit dari pertanyaan OP, di mana direktori tidak hanya berisi .pngfile.

The sed -nTeknik ini sering berguna dalam kasus di mana Anda mungkin sebaliknya menggunakan grep + sed.

Norman Gray
sumber
Saya suka bagaimana perawatan yang Anda gunakan untuk menulis jawaban Anda. Solusi ini akan menghadirkan masalah dengan nama file termasuk baris baru , itu tidak akan mencetak bagian pertama dari nama. Terlebih lagi jika itu adalah nastier dengan kunci ( .png) sebelum char baris baru: dalam hal ini Anda akan mencetak bagian itu tanpa png, tidak hanya menghapus bagian terakhir. Sering disarankan untuk menghindari mem-parsing (dan menyalurkan) output lskarena masalah dapat disembunyikan di tempat yang tidak Anda pikirkan ...
Hastur
2
@Hastur Anda benar, pada prinsipnya, dan halaman terkenal tentang jangan uraikan daftar masalah lebih lanjut (dan solusi) ketika menyerahkan nama file patologis. Tetapi cara terbaik penanganannya adalah menghindari memiliki nama file patologis (doh!); dan jika Anda tidak bisa, atau jika Anda harus kuat terhadap mereka, maka gunakan findatau - mungkin lebih baik - menggunakan bahasa yang lebih kuat daripada shmengelolanya (fakta bahwa sh dapat melakukan segalanya tidak berarti bahwa itu adalah pilihan terbaik di masing-masing kasus). Shell dirancang untuk kegunaan pertama.
Norman Gray
Saya setuju, pada prinsipnya, tentang kegunaan, tetapi varian ini gagal ketika Anda memiliki nama file dengan setiap baris baru di dalamnya. Ini dapat dengan mudah terjadi tanpa disadari, misalnya, ketika Anda menyalin dan menempelkan baris dari pdf dalam GUI, Jadi Anda hanya berpikir untuk menghindari nama file patologis .
Hastur
Selain itu IMHO Sangat mudah untuk mulai menguraikan ls, tetapi sedang terjadi masalah di masa depan. Seringkali kita membuat skrip yang akan kita gunakan nanti, ketika kita sudah akan melupakan batasnya ... (itu manusia, itu biasa). Saya mengusulkan sebuah findcontoh (dengan -execdan tanpa pipa) bahkan jika saya anggap lebih baik (karena shell murni) menjawab cuonglm ini satu , solid dan POSIX compliant.
Hastur
@ Rushur: masalah-masalah masa depan itu akan muncul. Banyak hal dalam sistem tidak kuat terhadap file dengan baris baru. Misalnya coba gunakan locateatau makepada mereka.
reinierpost
8

Anda hanya dapat menggunakan perintah BASH untuk melakukan itu (tanpa alat eksternal).

for file in *; do echo "${file%.*}"; done 

Ini berguna ketika Anda tanpa / usr / bin dan berfungsi baik untuk nama file seperti this.is.image.png dan untuk semua ekstensi.

Luciano Andress Martini
sumber
6

Tidaklah aman untuk menguraikan lsatau mengirim pipa find[ 1 , 2 ]

Tidaklah aman untuk mem-parsing (dan mem-pipe) output lsatau find, terutama karena mungkin untuk menemukan dalam nama file karakter yang tidak biasa sebagai baris baru , tab ... Di sini siklus shell murni akan bekerja [ cuonglm ] .
Bahkan findperintah yang tidak disalurkan dengan opsi -execakan berfungsi:

find ./*.png  -exec  basename {} .png  \;

Pembaruan / Catatan : Anda dapat menggunakan find .bahkan untuk mencari file yang tersembunyi, atau find ./*.pnghanya untuk mendapatkan file yang tidak tersembunyi. Dengan find *.png -exec ...Anda dapat memiliki masalah dalam hal itu ada file bernama .pngkarena find akan mendapatkannya sebagai opsi. Anda dapat menambahkan -maxdepth 0untuk menghindari turun di direktori bernama Dir_01.png, atau find ./*.png -prune -exec ...ketika maxdepth tidak diizinkan (terima kasih Stéphane). Jika Anda ingin menghindari daftar direktori tersebut, Anda harus menambahkan opsi -type f(yang juga akan mengecualikan jenis file non-reguler lainnya). Coba lihat untuk manpanorama yang lebih lengkap tentang semua opsi yang tersedia, dan ingatlah untuk memeriksa kapan mereka sesuai dengan POSIX, untuk portabilitas yang lebih baik.

Beberapa kata lagi

Bisa terjadi, misalnya, bahwa menyalin judul dari dokumen dan menempelkan ke nama file, satu atau lebih baris baru akan selesai dalam nama file itu sendiri. Kita bisa sangat sial sehingga judul bisa mengandung bahkan kunci yang harus kita gunakan sebelum baris baru:

The new art of working on .png
files and other formats.

Jika Anda ingin menguji, Anda dapat membuat nama file seperti ini dengan perintah

touch "A file with two lines"$'\n'"and This is the second.png"
touch "The new art of working on .png"$'\n'"files and other formats.png"

Sederhana /bin/ls *pngakan menampilkan ?bukan karakter yang tidak dapat dicetak

A file with two lines?and This is the second.png
The new art of working on .png?files and other formats.png

Dalam semua kasus di mana Anda akan pipa output dari lsatau findperintah berikut tidak akan memiliki petunjuk untuk memahami jika garis ini berasal dari baru nama berkas atau jika ia mengikuti baris baru karakter dalam preseden nama berkas . Sebuah jahat nama memang, tapi masih satu hukum.

Siklus shell dengan shell Parameter-Expansion,, ${parameter%word}dalam varian dengan printfatau echoakan bekerja [ cuonglm ], [ Anthon1 ] .

for f in *.png; do printf "%s\n" "${f%.png}" ; done

Dari halaman manual Ekspansi Parameter Shell [ 3 ]

$ {parameter% word}
$ {parameter %% kata}

... hasil ekspansi adalah nilai parameter dengan pola pencocokan terpendek (case '%') atau pola pencocokan terpanjang (case '%%') dihapus.

Cepat
sumber
Juga hasil dari findperintah Anda sedikit variabel (misalnya jika ada direktori yang disebut files.png)
1
Dear @BroSlow, ketika saya menulis jawaban di atas, saya mencoba 13 (semua) varian lain yang hadir pada saat itu, dengan baris perintah, dalam sebuah skrip, diluncurkan sebagai argumen doa shell. Tolong lakukan hal yang sama dan beri tahu saya jika mereka berperilaku seperti yang Anda harapkan. Saya melakukan tes dengan bash 4.3.11, lari 0,5,7-4, zsh (bila perlu) 5.0.2. Anda merasa bebas untuk membaca posting ini yang menambahkan sesuatu yang lebih. Saya setuju tentang catatan piping output find, untuk ini saya sarankan-exec , dan saya menulis dalam judul. :-).
Hastur
Baca ulang wiki lagi. Saya masih berpikir Anda perlu menyalurkan contoh Anda, karena itulah yang sedang dibahas di sini. Dan untuk sebagian besar versi modern lstidak ada masalah apa pun ketika output disalurkan atau dialihkan, tetapi sebagaimana disebutkan dalam wiki mungkin tidak bekerja untuk semua. Sebagian besar hanya akan memasukkan ?menggantikan karakter khusus ketika output dikirim ke terminal. yaitu Lakukan echo *.png | od -cdan ls *.png | od -c. Masalah baris baru bukan masalah dengan ls, ini masalah dengan perintah apa pun yang tidak nol berakhir di kedua sisi pipa.
1
printf "${f%.png}\n"salah. Argumen pertama adalah format, Anda tidak boleh menggunakan data variabel di sana. Bahkan dapat dilihat sebagai kerentanan DoS (coba dengan %1000000000s.pngfile misalnya).
Stéphane Chazelas
Anda perlu find ./*.png -prune -exec...atau Anda akan memiliki masalah dengan nama file yang dimulai dengan -(dan file-file dari direktori jenis, perhatikan bahwa -maxdepthini tidak portabel)
Stéphane Chazelas
4

bukankah itu cukup?

ls -1 | sed 's/\.png//g'

atau secara umum, ini

ls -1 | sed 's/\.[a-z]*//g'

akan menghapus semua ekstensi

Rohail Abbas
sumber
Itu tetapi solusi lain juga bekerja.
Colin
Saya bermaksud mengatakan, pertanyaan Anda dimulai dengan ls -1, jadi ls -1 harus melakukan itu. :)
Rohail Abbas
Tidak -1perlu, menjadi standar di sini.
jlliagre
@Rohail Abbas Tapi tidak semua sistem telah diinstal?
Colin
1
Memang, tetapi lsapakah itu tetap tanpa opsi itu ketika outputnya tidak menjadi terminal, yang terjadi di sini.
jlliagre
3

Gunakan rev:

ls -1 | rev | cut -f 2- -d "." | rev

revmembalikkan semua string (garis); Anda memotong semuanya setelah yang pertama '.' dan rev kembali sisa yang tersisa.

Jika Anda ingin grep'alma':

ls -1 | rev | cut -f 2- -d "." | rev | grep 'alma'
Tom Solid
sumber
Tidak -1perlu, menjadi standar di sini.
jlliagre
2
Ini gagal pada file bernamaMy.2016.Summer.Vacation.png
David Conrad
@ Davidvidon salah saya: / Saya telah mengoreksicut -f 2-
Tom Solid
Sekarang ini berfungsi dengan file itu tetapi belum dengan file dengan .pngdan baris baru setelah ... Disarankan untuk menghindari mengurai lskarena suka menyembunyikan kejutan dengan baik ... :-)
Hastur
2

Jika saya tahu direktori hanya memiliki file dengan .png sebagai ekstensi, saya hanya akan menjalankan: ls | awk -F. '{print $1}'

Ini akan mengembalikan "bidang" pertama untuk apa pun di mana ada namafile. Ekstensi.

Contoh:

[rsingh@rule51 TESTDIR]$ ls
10.png  1.png  2.png  3.png  4.png  5.png  6.png  7.png  8.png  9.png

[rsingh@rule51 TESTDIR]$ ls | awk -F. '{print $1}'
10
1
2
3
4
5
6
7
8
9
rsingh
sumber
Sayangnya itu akan gagal pada semua nama file dengan lebih dari satu ., seperti Image.1.pngdan bahkan pada yang dengan nama tidak bagus , dengan karakter khusus di dalamnya. sebagai baris baru atau salah satu yang akan Anda gunakan sebagai (input) pemisah record di awk, RS. Disarankan untuk tidak mengurai lsoutput karena suka menyembunyikan masalah yang akan muncul ketika Anda tidak akan mengharapkan. Anda dapat membaca lebih lanjut di referensi 1 atau 2 tersebut . BTW ide bagus untuk menggunakan awk ... Saya menaruh beberapa contoh dalam satu jawaban.
Hastur
Benar, bagaimanapun, mengingat sampel yang disediakan oleh Colin itu akan berfungsi dengan baik. Untuk membuatnya berfungsi untuk kasus yang Anda sarankan, saya mungkin akan mengubahnya ke: [rsingh @ rule51 TESTDIR] $ ls | sed -e 's / .png $ //' 10 1 2 3 4 5 6 7 8 9 harry.the.bunny whats.a.png.filename Tidak berusaha menjadi sulit, tetapi mengingat kebutuhan Colin, saya tidak yakin apa masalah yang akan parsing ls.
rsingh
maaf ... Saya baru sadar saya tidak menunjukkan direktori dengan file sebelum sed memodifikasi output 'ls' [rsingh @ rule51 TESTDIR] $ ls 10.png 2.png 4.png 4.png 6.png 6.png 8.png harry.the.bunny.png 1.png 3.png 5.png 7.png 9.png whats.a.png.filename.png [rsingh @ rule51 TESTDIR] $ ls | sed -e 's / .png $ //' 10 1 2 3 4 5 6 7 8 9 harry.the.bunny whats.a.png.filename
rsingh
NOTE1 Anda perlu untuk melarikan diri .di \.dalam sed -e 's/\.png$//', tapi sehingga menjadi jawaban hanya ditulis. :-( note2 Anda dapat mencoba menggunakan awkdengan sesuatu seperti ls | awk -F. '{if ($NF=="png") {for (i=1;i<NF-1;i++) printf("%s.", $i) ; printf $(NF-1)"\n"}}'... tetapi Anda akan selalu memiliki masalah yang awk tidak tahu apakah garis itu tiba mengikuti atau tidak baris baru di dalam nama file. Saya mencoba mengatakan lebih baik dalam jawaban saya .
Hastur
Terima kasih Hastur, saya melewatkan itu :). Juga, saya membuang penggunaan awk demi sed dalam kasus ini.
rsingh
2

menurut komentar Anda "Saya memiliki file teks di mana semua nama terdaftar tanpa ekstensi. Saya ingin membuat skrip PHP yang membandingkan file teks dengan folder untuk melihat file mana yang hilang":

for file in $(cat yourlist) ; do
  [ -f "${file}.png" ] || {
    echo "$file : listed in yourlist, but missing in the directory"
  }
done
#assumes that filenames have no space...
# otherwise use instead:
#  while IFS= read file ; do ...(same inner loop as above)... ; done < yourlist

dan sebaliknya:

for file in *.png ; do
  grep "^${file%.png}$" yourlist >/dev/null || {
    echo "$file: present in the directory but not listed in yourlist"
  }
done
#I assume there are no spaces/tabs/? before/after names in 'yourlist'. Change the script accordingly if there are some (or sanitize the list)
Olivier Dulac
sumber
1

ls -l | sed 's/\.png$//'

Apakah metode yang paling akurat seperti yang disorot oleh @roaima. Tanpa lolos \.pngfile bernama a_png.pngakan terdaftar sebagai: a_.

kata mutiara
sumber
menggunakan ls -l, seperti yang Anda lakukan, memberikan detail file, bukan itu yang ditanyakan OP.
Anthon
1

Garis shell sederhana (ksh, bash atau zsh; bukan dash):

set -- *.png; printf '%s\n' "${@%.png}"

Fungsi sederhana (dari Tanpa Ekstensi):

ne(){ set -- *.png; printf '%s\n' "${@%.png}"; }

Atau fungsi yang menghapus ekstensi apa pun yang diberikan (png secara default):

ne(){ ext=${1:-png}; set -- *."$ext"; printf '%s\n' "${@%.${ext}}"; }

Digunakan sebagai:

ne jpg

Jika outputnya adalah tanda bintang *, tidak ada file dengan ekstensi itu.


sumber
1

Anda dapat mencoba umpan berikut ini untuk melihat apakah superator Anda adalah "." dan karena semua file Anda akan memiliki name.png Anda mencetak kolom pertama:
ls | awk -F"." '{print $1}'

igiannak
sumber
-1

Jika Anda memiliki akses ke sed, ini lebih baik karena akan menghapus ekstensi file terakhir, apa pun itu (png, jpg, tiff, dll ...)

ls | sed -e 's/\..*$//'
Hristo Mohamed
sumber
7
Istirahat untuk nama file seperti this.is.a.dotty.txt. Coba s/\.[^.]*$//saja.
roaima