Bagaimana cara menulis skrip untuk memindahkan hanya 20 file tertua dari satu folder ke folder lain? Apakah ada cara untuk mengambil file tertua di folder?
bash
shell
shell-script
files
timestamps
pengguna11598
sumber
sumber
atime
(akses terakhir),ctime
(perubahan izin terakhir), danmtime
(terakhir dimodifikasi) ... misalnya.ls -t
dan temukanprintf "%T"
penggunaannyamtime
... Sepertinya, menurut tautan ini , bahwaext4
partisi saya mampu menangani tanggal pembuatan, tetapils
danfind
dan danstat
belum memiliki opsi yang sesuai (belum) ...Jawaban:
Parsing output
ls
adalah tidak dapat diandalkan .Sebagai gantinya, gunakan
find
untuk mencari file dansort
memesannya dengan stempel waktu. Sebagai contoh:Apa yang sedang dilakukan semua ini?
Pertama,
find
perintah menempatkan semua file dan direktori di direktori saat ini (.
), tetapi tidak di subdirektori dari direktori saat ini (-maxdepth 1
), kemudian mencetak:Stempel waktu itu penting. Penentu
%T@
format untuk-printf
dipecah menjadiT
, yang menunjukkan "Waktu modifikasi terakhir" dari file (mtime) dan@
, yang menunjukkan "Detik sejak 1970", termasuk detik pecahan.Ruang hanyalah pembatas yang sewenang-wenang. Path lengkap ke file adalah agar kita dapat merujuknya nanti, dan karakter NULL adalah terminator karena itu adalah karakter ilegal dalam nama file dan dengan demikian memberi tahu kami bahwa kami mencapai ujung path ke mengajukan.
Saya telah menyertakan
2>/dev/null
agar file yang pengguna tidak memiliki izin untuk mengaksesnya dikecualikan, tetapi pesan kesalahan tentang mereka yang dikecualikan ditekan.Hasil dari
find
perintah adalah daftar semua direktori di direktori saat ini. Daftar ini disalurkan kesort
yang diperintahkan untuk:-z
Perlakukan NULL sebagai karakter terminator garis alih-alih baris baru.-n
Sortir secara numerikKarena detik-sejak-1970 selalu naik, kami ingin file yang cap waktu-nya adalah angka terkecil. Hasil pertama dari
sort
akan menjadi garis yang berisi stempel waktu bernomor terkecil. Yang tersisa hanyalah mengekstrak nama file.Hasil dari
find
,sort
pipeline dilewatkan melalui proses substitusi kewhile
tempat itu dibaca seolah-olah itu adalah file di stdin.while
pada gilirannya memanggilread
untuk memproses input.Dalam konteks
read
kami mengaturIFS
variabel menjadi tidak ada, yang berarti spasi tidak akan ditafsirkan secara tidak tepat sebagai pembatas.read
dikatakan-r
, yang menonaktifkan ekspansi, dan-d $'\0'
, yang membuat pembatas akhir-line NULL, cocok dengan output darifind
,sort
pipeline kami.Potongan data pertama, yang mewakili jalur file tertua yang didahului oleh stempel waktu dan spasinya, dibaca ke dalam variabel
line
. Selanjutnya, substitusi parameter digunakan dengan ekspresi#*
, yang hanya mengganti semua karakter dari awal string hingga spasi pertama, termasuk spasi, tanpa spasi. Ini menghilangkan tanda waktu modifikasi, hanya menyisakan path lengkap ke file.Pada titik ini nama file disimpan
$file
dan Anda dapat melakukan apa pun yang Anda suka dengannya. Setelah selesai melakukan sesuatu dengan$file
parawhile
pernyataan kehendak loop danread
perintah akan dieksekusi lagi, penggalian bongkahan berikutnya dan nama file selanjutnya.Apakah tidak ada cara yang lebih sederhana?
Tidak. Cara-cara yang lebih sederhana itu buggy.
Jika Anda menggunakan
ls -t
dan mem-pipe kehead
atautail
(atau apa saja ) Anda akan merusak file dengan baris baru dalam nama file. Jika Andamv $(anything)
kemudian file dengan spasi putih dalam nama akan menyebabkan kerusakan. Jika Andamv "$(anything)"
kemudian file dengan trailing baris baru pada nama akan menyebabkan kerusakan. Jika Andaread
tanpa-d $'\0'
maka Anda akan merusak file dengan spasi putih di nama mereka.Mungkin dalam kasus-kasus tertentu Anda tahu pasti bahwa cara yang lebih sederhana sudah cukup, tetapi Anda tidak boleh menulis asumsi seperti itu di skrip jika Anda dapat menghindari melakukannya.
Larutan
Sebut seperti:
Untuk memindahkan 20 file terlama dari
/var/log/foo/
ke/mnt/backup/
.Perhatikan bahwa saya termasuk file dan direktori. Untuk file hanya menambah
-type f
kefind
doa.Terima kasih
Terima kasih kepada enzotib dan Павел Танков untuk peningkatan pada jawaban ini.
sumber
-n
. Setidaknya dalam versi saya, itu tidak mengurutkan angka desimal dengan benar. Anda juga harus menghapus titik di tanggal atau penggunaan-printf '%TY-%Tm-%TdT%TH:%TM:%TS %p\0' | sort -rz
, tanggal ISO, atau yang lainnya..
harus tidak relevan bagi Anda.) Akan lebih jelas untuk mengatakansort -z -n -t. -k1
.%TS
juga menunjukkan "bagian pecahan" yang akan berada dalam formulir00.0000000000
, sehingga Anda juga kehilangan rincian. GNU baru-baru inisort
dapat menyelesaikan masalah ini dengan menggunakan-V
untuk "versi sortir", yang akan menangani jenis floating point seperti yang diharapkan.%T@
juga akan berfungsi, karena itu adalah nol-empuk.Paling mudah di zsh, di mana Anda dapat menggunakan
Om
kualifikasi glob untuk mengurutkan pertandingan berdasarkan tanggal (tertua lebih dulu) dan[1,20]
kualifikasi untuk mempertahankan hanya 20 pertandingan pertama:Tambahkan
D
kualifikasi jika Anda ingin menyertakan file dot juga. Tambahkan.
jika Anda hanya ingin mencocokkan file biasa dan bukan direktori.Jika Anda tidak memiliki zsh, inilah Perl one-liner (Anda dapat melakukannya dalam kurang dari 80 karakter, tetapi dengan biaya lebih lanjut dalam kejelasan):
Dengan hanya alat POSIX atau bahkan bash atau ksh, menyortir file berdasarkan tanggal adalah hal yang menyakitkan. Anda dapat melakukannya dengan mudah
ls
, tetapi penguraian outputnyals
bermasalah, jadi ini hanya berfungsi jika nama file hanya berisi karakter yang dapat dicetak selain baris baru.sumber
Gabungkan
ls -t
output dengantail
atauhead
.Contoh sederhana, yang hanya berfungsi jika semua nama file hanya berisi karakter yang dapat dicetak selain spasi putih dan
\[*?
:sumber
ls -1Atr
touch $'foo\n*'
. Apa yang terjadi jika Anda menjalankan mv "$ (ls)" dengan file yang ada di sana?Anda dapat menggunakan GNU find untuk ini:
Di mana menemukan mencetak waktu modifikasi (dalam detik dari tahun 1970) dan nama setiap file dari direktori saat ini, output diurutkan sesuai dengan bidang pertama, 20 yang tertua disaring dan dipindahkan ke
dest_dir
. Hapusecho
jika Anda telah menguji baris perintah.sumber
Belum ada yang memposting contoh bash yang melayani chars baris baru tertanam (embedded anything) di nama file, jadi inilah salah satu. Ini memindahkan 3 file biasa tertua (mdate)
Ini adalah cuplikan uji-data
Berikut ini cuplikan hasil-cek
sumber
Ini paling mudah dilakukan dengan GNU
find
. Saya menggunakannya setiap hari di DVR Linux saya untuk menghapus rekaman dari sistem pengawasan video saya lebih lama dari satu hari.Berikut ini sintaksnya:
Ingat bahwa
find
mendefinisikan satu hari sebagai 24 jam dari waktu eksekusi. Karenanya file yang terakhir dimodifikasi pada jam 11 malam tidak akan dihapus pada jam 1 pagi.Anda bahkan dapat menggabungkan
find
dengancron
, jadi penghapusan dapat dijadwalkan secara otomatis dengan menjalankan perintah berikut sebagai root:Anda selalu dapat memperoleh informasi lebih lanjut
find
dengan membaca halaman buku panduannya:sumber
karena jawaban lain tidak sesuai dengan tujuan saya dan pertanyaan, shell ini diuji pada CentOS 7:
sumber