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?
cp ./<the final 4 files> ~/
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?
cp ./<the final 4 files> ~/
Jawaban:
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 ).
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.
Ini akan menyalin empat file terakhir hanya ke direktori home dan, pada saat yang sama, menambahkan string a_ke nama file yang disalin:
a=(*)
for fname in "${a[@]: -4}"; do cp -- "$fname" ~/a_"$fname"; done
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.
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):
cp -- *(D.On[1,4]) ~
*([-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.
find . -maxdepth 1 -type f | sort | tail -n 4 | while read -r file; do cp "$file" ~/; done
Penjelasan:
find . -maxdepth 1 -type f
Temukan semua file di direktori saat ini.
sort
Mengurutkan menurut abjad.
tail -n 4
Hanya tampilkan 4 baris terakhir.
while read -r file; do cp "$file" ~/; done
Mengulang setiap baris melakukan perintah salin.
Selama Anda menemukan shell sortable, Anda bisa melakukan:
set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
cp "$@" /path/to/target/dir
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.
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 ..
set 1 2 3 4 5kasus itu akan menggeser 1jauh, tetapi dalam sebuah set 1 2 3kasus, 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:
set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
pax -rws '|.*/|new_prefix|' "$@" /path/to/target/dir
... yang akan menerapkan sedsubtitusi-gaya ke semua nama file saat disalin.
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 :
cp -- $(ls | tail -n 4) ~/
a_ke SEMUA empat file? Saya sudah mencoba echo "a_$(ls | tail -n 4)", tetapi hanya menambahkan file pertama.
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".
Ini adalah solusi bash sederhana tanpa kode loop apa pun. Salin 4 file terakhir dari direktori saat ini ke tujuan:
$ find . -maxdepth 1 -type f |tail -n -4|xargs cp -t "$destdir"
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