Bagaimana cara mendapatkan file N terakhir dalam direktori?
15
Saya memiliki banyak file yang dipesan berdasarkan nama file dalam direktori. Saya ingin menyalin file N akhir (katakanlah, N = 4) ke direktori rumah saya. Bagaimana saya harus melakukannya?
Pada semua jawaban di bawah, Anda mungkin perlu menambahkan "LC_ALL = ..." di depan perintah menggunakan rentang, sehingga rentang mereka menggunakan lokal yang tepat untuk Anda (lokal dapat berubah, dan urutan karakter kemudian dapat berubah banyak, mis. di beberapa tempat, [az] dapat berisi semua huruf alfabet kecuali untuk "Z", karena huruf-hurufnya dipesan: aAbBcC .... zZ. Variasi lain ada (huruf aksentuasi, dan susunan yang lebih eksotis) ) Pilihan yang biasa adalah:LC_ALL=C
Olivier Dulac
Jawaban:
23
Ini dapat dengan mudah dilakukan dengan array bash / ksh93 / zsh:
a=(*)
cp --"${a[@]: -4}"~/
Ini berfungsi untuk semua nama file yang tidak tersembunyi walaupun mengandung spasi, tab, baris baru, atau karakter sulit lainnya (dengan asumsi setidaknya ada 4 file yang tidak disembunyikan di direktori saat ini dengan bash ).
Bagaimana itu bekerja
a=(*)
Ini menciptakan array adengan semua nama file. Nama file yang dikembalikan oleh bash diurutkan berdasarkan abjad. (Saya berasumsi bahwa ini adalah apa yang Anda maksud dengan "dipesan dengan nama file." )
${a[@]: -4}
Ini mengembalikan empat elemen terakhir array a(asalkan array mengandung setidaknya 4 elemen bash).
cp -- "${a[@]: -4}" ~/
Ini menyalin empat nama file terakhir ke direktori home Anda.
Untuk menyalin dan mengganti nama secara bersamaan
Ini akan menyalin empat file terakhir hanya ke direktori home dan, pada saat yang sama, menambahkan string a_ke nama file yang disalin:
Salin dari direktori yang berbeda dan ganti nama juga
Jika kita menggunakan a=(./some_dir/*)alih-alih a=(*), maka kita memiliki masalah direktori yang dilampirkan ke nama file. Salah satu solusinya adalah:
a=(./some_dir/*)for f in"${a[@]: -4}";do cp "$f"~/a_"${f##*/}";done
Solusi lain adalah dengan menggunakan subkulit dan cdke direktori dalam subkulit:
(cd ./some_dir && a=(*)&&for f in"${a[@]: -4}";do cp --"$f"~/a_"$f";done)
Ketika subkulit selesai, shell mengembalikan kita ke direktori asli.
Memastikan bahwa pemesanan konsisten
Pertanyaannya menanyakan file "dipesan berdasarkan nama file". Pesanan itu, Olivier Dulac tunjukkan dalam komentar, akan bervariasi dari satu lokal ke yang lain. Jika penting untuk mendapatkan hasil yang independen dari pengaturan mesin, maka yang terbaik adalah menentukan lokal secara eksplisit ketika array adidefinisikan. Sebagai contoh:
LC_ALL=C a=(*)
Anda dapat mengetahui lokasi Anda saat ini dengan menjalankan localeperintah.
Jika Anda menggunakan zshAnda dapat melampirkan dalam tanda kurung ()daftar yang disebut kualifikasi glob yang memilih file yang diinginkan. Dalam kasus Anda, itu akan menjadi
cp *(On[1,4])~/
Di sini Onmengurutkan nama file secara alfabet dengan urutan terbalik dan [1,4]hanya mengambil 4 pertama dari mereka.
Anda dapat membuat ini lebih kuat dengan memilih hanya file biasa (tidak termasuk direktori, pipa dll) dengan ., dan juga dengan menambahkan --ke cpperintah untuk file memperlakukan yang namanya dimulai dengan -benar, sehingga:
cp --*(.On[1,4])~
Tambahkan Dkualifikasi jika Anda juga ingin mempertimbangkan file tersembunyi (dot-file):
@ StéphaneChazelas ya, mari kita perjelas untuk pembaca masa depan yang mengurutkan menurut abjad ke arah normal ( on) adalah perilaku default, jadi tidak perlu menentukan ini, dan -di depan angka-angkanya menghitung nama file dari bawah.
jimmij
7
Berikut adalah solusi menggunakan perintah bash yang sangat sederhana.
Ini sangat mirip dengan bashsolusi array-spesifik yang ditawarkan, tetapi harus portabel untuk setiap shell yang kompatibel dengan POSIX. Beberapa catatan tentang kedua metode:
Adalah penting bahwa Anda memimpin cpargumen cp --Anda tanpa mendapatkan salah satu dari .titik atau /di kepala setiap nama. Jika Anda gagal melakukan hal ini Anda berisiko terkemuka -dasbor di cpargumen pertama 's yang dapat diartikan sebagai pilihan dan menyebabkan operasi gagal atau sebaliknya membuat hasil yang tidak diinginkan.
Bahkan jika bekerja dengan direktori saat ini sebagai direktori sumber ini dengan mudah dilakukan seperti ... set -- ./*atau array=(./*).
Adalah penting ketika menggunakan kedua metode untuk memastikan Anda memiliki setidaknya banyak item dalam array arg Anda saat Anda mencoba untuk menghapus - Saya melakukannya di sini dengan ekspansi matematika. Satu- shiftsatunya yang terjadi jika setidaknya ada 4 item dalam array arg - dan kemudian hanya menggeser argumen pertama yang menghasilkan surplus 4 ..
Jadi dalam suatu set 1 2 3 4 5kasus itu akan menggeser 1jauh, tetapi dalam sebuah set 1 2 3kasus, itu tidak akan mengubah apa pun.
Misalnya: a=(1 2 3); echo "${a[@]: -4}"mencetak garis kosong.
Jika Anda menyalin dari satu direktori ke direktori lain, Anda dapat menggunakan pax:
Jika hanya ada file dan namanya tidak mengandung spasi atau baris baru (dan Anda belum dimodifikasi $IFS) atau karakter glob (atau beberapa karakter yang tidak dapat dicetak dengan beberapa implementasi ls), dan jangan mulai dengan ., maka Anda dapat melakukan ini :
Terima kasih! Berhasil. Apakah ada jauh untuk dengan cepat menambahkan awalan a_ke SEMUA empat file? Saya sudah mencoba echo "a_$(ls | tail -n 4)", tetapi hanya menambahkan file pertama.
Sibbs Gambling
@SibbsGambling Pendekatan saya tidak cocok untuk itu tetapi jawaban John1024 dapat dengan mudah disesuaikan dengan itu.
Hauke Laging
5
Secara umum parsing lsoutput dianggap sebagai bentuk buruk karena biasanya gagal mengerikan pada nama file yang tidak hanya berisi spasi, tetapi juga tab, baris baru, atau karakter lain yang valid tetapi "sulit".
HBruijn
2
@ HaukeLaging Sekalipun saat ini tidak menjadi masalah (karena saya tidak memiliki nama samaran "rumit" di direktori saya), saya mungkin punya dalam 2 tahun, dan tiba-tiba segalanya jatuh pada kaki saya ...
glglgl
1
Ini adalah solusi bash sederhana tanpa kode loop apa pun. Salin 4 file terakhir dari direktori saat ini ke tujuan:
Bagaimana Anda memastikan itu findmemberi Anda file dalam urutan yang diinginkan? Bagaimana Anda memastikan bahwa file yang mengandung karakter spasi putih (termasuk baris baru) dalam namanya ditangani dengan benar?
LC_ALL=C
Jawaban:
Ini dapat dengan mudah dilakukan dengan array bash / ksh93 / zsh:
Ini berfungsi untuk semua nama file yang tidak tersembunyi walaupun mengandung spasi, tab, baris baru, atau karakter sulit lainnya (dengan asumsi setidaknya ada 4 file yang tidak disembunyikan di direktori saat ini dengan
bash
).Bagaimana itu bekerja
a=(*)
Ini menciptakan array
a
dengan semua nama file. Nama file yang dikembalikan oleh bash diurutkan berdasarkan abjad. (Saya berasumsi bahwa ini adalah apa yang Anda maksud dengan "dipesan dengan nama file." )${a[@]: -4}
Ini mengembalikan empat elemen terakhir array
a
(asalkan array mengandung setidaknya 4 elemenbash
).cp -- "${a[@]: -4}" ~/
Ini menyalin empat nama file terakhir ke direktori home Anda.
Untuk menyalin dan mengganti nama secara bersamaan
Ini akan menyalin empat file terakhir hanya ke direktori home dan, pada saat yang sama, menambahkan string
a_
ke nama file yang disalin:Salin dari direktori yang berbeda dan ganti nama juga
Jika kita menggunakan
a=(./some_dir/*)
alih-aliha=(*)
, maka kita memiliki masalah direktori yang dilampirkan ke nama file. Salah satu solusinya adalah:Solusi lain adalah dengan menggunakan subkulit dan
cd
ke direktori dalam subkulit:Ketika subkulit selesai, shell mengembalikan kita ke direktori asli.
Memastikan bahwa pemesanan konsisten
Pertanyaannya menanyakan file "dipesan berdasarkan nama file". Pesanan itu, Olivier Dulac tunjukkan dalam komentar, akan bervariasi dari satu lokal ke yang lain. Jika penting untuk mendapatkan hasil yang independen dari pengaturan mesin, maka yang terbaik adalah menentukan lokal secara eksplisit ketika array
a
didefinisikan. Sebagai contoh:Anda dapat mengetahui lokasi Anda saat ini dengan menjalankan
locale
perintah.sumber
Jika Anda menggunakan
zsh
Anda dapat melampirkan dalam tanda kurung()
daftar yang disebut kualifikasi glob yang memilih file yang diinginkan. Dalam kasus Anda, itu akan menjadiDi sini
On
mengurutkan nama file secara alfabet dengan urutan terbalik dan[1,4]
hanya mengambil 4 pertama dari mereka.Anda dapat membuat ini lebih kuat dengan memilih hanya file biasa (tidak termasuk direktori, pipa dll) dengan
.
, dan juga dengan menambahkan--
kecp
perintah untuk file memperlakukan yang namanya dimulai dengan-
benar, sehingga:Tambahkan
D
kualifikasi jika Anda juga ingin mempertimbangkan file tersembunyi (dot-file):sumber
*([-4,-1])
.on
) adalah perilaku default, jadi tidak perlu menentukan ini, dan-
di depan angka-angkanya menghitung nama file dari bawah.Berikut adalah solusi menggunakan perintah bash yang sangat sederhana.
Penjelasan:
Temukan semua file di direktori saat ini.
Mengurutkan menurut abjad.
Hanya tampilkan 4 baris terakhir.
Mengulang setiap baris melakukan perintah salin.
sumber
Selama Anda menemukan shell sortable, Anda bisa melakukan:
Ini sangat mirip dengan
bash
solusi array-spesifik yang ditawarkan, tetapi harus portabel untuk setiap shell yang kompatibel dengan POSIX. Beberapa catatan tentang kedua metode:Adalah penting bahwa Anda memimpin
cp
argumencp --
Anda tanpa mendapatkan salah satu dari.
titik atau/
di kepala setiap nama. Jika Anda gagal melakukan hal ini Anda berisiko terkemuka-
dasbor dicp
argumen pertama 's yang dapat diartikan sebagai pilihan dan menyebabkan operasi gagal atau sebaliknya membuat hasil yang tidak diinginkan.set -- ./*
atauarray=(./*)
.Adalah penting ketika menggunakan kedua metode untuk memastikan Anda memiliki setidaknya banyak item dalam array arg Anda saat Anda mencoba untuk menghapus - Saya melakukannya di sini dengan ekspansi matematika. Satu-
shift
satunya yang terjadi jika setidaknya ada 4 item dalam array arg - dan kemudian hanya menggeser argumen pertama yang menghasilkan surplus 4 ..set 1 2 3 4 5
kasus itu akan menggeser1
jauh, tetapi dalam sebuahset 1 2 3
kasus, itu tidak akan mengubah apa pun.a=(1 2 3); echo "${a[@]: -4}"
mencetak garis kosong.Jika Anda menyalin dari satu direktori ke direktori lain, Anda dapat menggunakan
pax
:... yang akan menerapkan
sed
subtitusi-gaya ke semua nama file saat disalin.sumber
Jika hanya ada file dan namanya tidak mengandung spasi atau baris baru (dan Anda belum dimodifikasi
$IFS
) atau karakter glob (atau beberapa karakter yang tidak dapat dicetak dengan beberapa implementasils
), dan jangan mulai dengan.
, maka Anda dapat melakukan ini :sumber
a_
ke SEMUA empat file? Saya sudah mencobaecho "a_$(ls | tail -n 4)"
, tetapi hanya menambahkan file pertama.ls
output dianggap sebagai bentuk buruk karena biasanya gagal mengerikan pada nama file yang tidak hanya berisi spasi, tetapi juga tab, baris baru, atau karakter lain yang valid tetapi "sulit".Ini adalah solusi bash sederhana tanpa kode loop apa pun. Salin 4 file terakhir dari direktori saat ini ke tujuan:
sumber
find
memberi Anda file dalam urutan yang diinginkan? Bagaimana Anda memastikan bahwa file yang mengandung karakter spasi putih (termasuk baris baru) dalam namanya ditangani dengan benar?