Çıktısını Ayrıştırma lsolan güvenilir değildir .
Bunun yerine, finddosyaları bulmak ve sortzaman 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, findkomutlar 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 -printfayırır Tdosyası (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.
findKomutun 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, sortboru hattı üzerinden geçirilir süreç ikamesi için whilestdin bir dosya sanki okumak nerede. whilesırayla readgirişi işlemek için çağırır .
Değişkeni bağlamında hiçbir şeye readayarlayamayız, IFSyani 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 readsöylenir .-r-d $'\0'findsort
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 $fileve 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.$filewhileread
Daha basit bir yol yok mu?
Hayır. Daha basit yollar hatalı.
Eğer kullanıyorsanız ls -tve boru için headya 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 readolmadan -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 fiçin findçağırma.
Teşekkürler
Bu cevabı geliştirdikleri için enzotib ve Павел Танков'a teşekkürler .