Çıktısını Ayrıştırma ls
olan güvenilir değildir .
Bunun yerine, find
dosyaları bulmak ve sort
zaman damgasına göre sıralamak için kullanın. Örneğin:
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)
Bütün bunlar ne yapıyor?
İlk olarak, find
komutlar geçerli dizindeki ( .
) tüm dosyaları ve dizinleri bulur , ancak geçerli dizinin ( ) alt dizinlerinde bulmaz -maxdepth 1
, sonra yazdırır:
- Bir zaman damgası
- Bir boşluk
- Dosyanın göreceli yolu
- NULL karakter
Zaman damgası önemlidir. %T@
İçin biçim belirteci -printf
ayırır T
dosyası (mtime) ve "Son değişiklik zamanı" belirtir, @
fraksiyonel saniye dahil "1970 yılından bu yana Saniye", gösterir.
Alan sadece keyfi bir sınırlayıcıdır. Dosyanın tam yolu, daha sonra başvurabilmemiz için NULL karakteri bir sonlandırıcıdır çünkü dosya adında geçersiz bir karakterdir ve bu nedenle dosya yolunun sonuna ulaştığımızdan emin olmanızı sağlar. dosya.
dahil ettim 2>/dev/null
kullanıcı erişim izni yok dosyalar dışlanır, ama onlar hakkında hata mesajlar engellenmektedir dışlanmadan böylece.
find
Komutun sonucu , geçerli dizindeki tüm dizinlerin listesidir. Liste, aşağıdakilere bildirilecek şekilde yönlendirilir sort
:
-z
NULL, satırsonu yerine satır sonlandırıcı karakteri olarak kabul edilir.
-n
Sayısal olarak sırala
1970'den bu yana saniyeler her zaman yükseldiğinden, zaman damgası en küçük sayı olan dosyayı istiyoruz. İlk sonuçsort
, en küçük numaralı zaman damgasını içeren satır olacaktır. Geriye kalan tek şey dosya adını çıkarmaktır.
Sonuçları find
, sort
boru hattı üzerinden geçirilir süreç ikamesi için while
stdin bir dosya sanki okumak nerede. while
sırayla read
girişi işlemek için çağırır .
Değişkeni bağlamında hiçbir şeye read
ayarlayamayız, IFS
yani boşluk boşluk sınırlayıcı olarak yorumlanmayacaktır. kaçış genişlemesini devre dışı bırakan ve satır sonu sınırlayıcısını NULL yapan , boru hattımızın çıktısını eşleştiren read
söylenir .-r
-d $'\0'
find
sort
Zaman damgası ve boşluğundan önce gelen en eski dosya yolunu temsil eden ilk veri parçası değişkene okunur line
. Daha sonra, dizinin başlangıcından boşluk dahil olmak üzere ilk boşluğa kadar tüm karakterleri hiçbir şeyle değiştirmeyen ifade ile parametre ikamesi kullanılır #*
. Bu, değişiklik zaman damgasını kaldırarak dosyanın tam yolunu bırakır.
Bu noktada dosya adı saklanır $file
ve onunla istediğiniz her şeyi yapabilirsiniz. Eğer bir şey yapmak tamamladığınızda deyimi irade döngü ve komut sonraki öbek ve sonraki dosya adını çıkararak tekrar çalıştırılacaktır.$file
while
read
Daha basit bir yol yok mu?
Hayır. Daha basit yollar hatalı.
Eğer kullanıyorsanız ls -t
ve boru için head
ya tail
(veya herhangi bir şey ) Eğer dosya adlarında satırsonu ile dosyalar üzerinde kırarım. Daha mv $(anything)
sonra adında boşluk bulunan dosyalar kırılmaya neden olur. Daha mv "$(anything)"
sonra adında sondaki yeni satırları olan dosyalar kırılmaya neden olur. Eğer read
olmadan -d $'\0'
o zaman adlarında boşluk ile dosyalar üzerinde kırarım.
Belki belirli durumlarda, daha basit bir yolun yeterli olduğundan emin olabilirsiniz, ancak bunu önlemek için komut dosyalarına asla böyle varsayımlar yazmamalısınız.
Çözüm
#!/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)
Şöyle arayın:
move-oldest /mnt/backup/ /var/log/foo/ 20
En eski 20 dosyayı konumundan konumuna taşımak /var/log/foo/
için /mnt/backup/
.
Dosya ve dizinler eklediğimi unutmayın. Dosyalar için yalnızca eklemek -type f
için find
çağırma.
Teşekkürler
Bu cevabı geliştirdikleri için enzotib ve Павел Танков'a teşekkürler .