Parsing output lsadalah tidak dapat diandalkan .
Sebagai gantinya, gunakan finduntuk mencari file dan sortmemesannya dengan stempel waktu. Sebagai contoh:
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
# do something with $file here
done < <(find . -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
Apa yang sedang dilakukan semua ini?
Pertama, findperintah menempatkan semua file dan direktori di direktori saat ini ( .), tetapi tidak di subdirektori dari direktori saat ini ( -maxdepth 1), kemudian mencetak:
- Stempel waktu
- Sebuah ruang
- Jalur relatif ke file
- Karakter NULL
Stempel waktu itu penting. Penentu %T@format untuk -printfdipecah menjadi T, 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/nullagar file yang pengguna tidak memiliki izin untuk mengaksesnya dikecualikan, tetapi pesan kesalahan tentang mereka yang dikecualikan ditekan.
Hasil dari findperintah adalah daftar semua direktori di direktori saat ini. Daftar ini disalurkan ke sortyang diperintahkan untuk:
-z Perlakukan NULL sebagai karakter terminator garis alih-alih baris baru.
-n Sortir secara numerik
Karena detik-sejak-1970 selalu naik, kami ingin file yang cap waktu-nya adalah angka terkecil. Hasil pertama dari sortakan menjadi garis yang berisi stempel waktu bernomor terkecil. Yang tersisa hanyalah mengekstrak nama file.
Hasil dari find, sortpipeline dilewatkan melalui proses substitusi ke whiletempat itu dibaca seolah-olah itu adalah file di stdin. whilepada gilirannya memanggil readuntuk memproses input.
Dalam konteks readkami mengatur IFSvariabel menjadi tidak ada, yang berarti spasi tidak akan ditafsirkan secara tidak tepat sebagai pembatas. readdikatakan -r, yang menonaktifkan ekspansi, dan -d $'\0', yang membuat pembatas akhir-line NULL, cocok dengan output dari find, sortpipeline 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 $filedan Anda dapat melakukan apa pun yang Anda suka dengannya. Setelah selesai melakukan sesuatu dengan $filepara whilepernyataan kehendak loop dan readperintah 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 -tdan mem-pipe ke headatau tail(atau apa saja ) Anda akan merusak file dengan baris baru dalam nama file. Jika Anda mv $(anything)kemudian file dengan spasi putih dalam nama akan menyebabkan kerusakan. Jika Anda mv "$(anything)"kemudian file dengan trailing baris baru pada nama akan menyebabkan kerusakan. Jika Anda readtanpa -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
#!/usr/bin/env bash
# move to the first argument
dest="$1"
# move from the second argument or .
source="${2-.}"
# move the file count in the third argument or 20
limit="${3-20}"
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
echo mv "$file" "$dest"
let limit-=1
[[ $limit -le 0 ]] && break
done < <(find "$source" -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
Sebut seperti:
move-oldest /mnt/backup/ /var/log/foo/ 20
Untuk memindahkan 20 file terlama dari /var/log/foo/ke /mnt/backup/.
Perhatikan bahwa saya termasuk file dan direktori. Untuk file hanya menambah -type fke finddoa.
Terima kasih
Terima kasih kepada enzotib dan Павел Танков untuk peningkatan pada jawaban ini.