Bir dizindeki son N dosyası nasıl alınır?


15

Bir dizinde dosya adına göre sıralanan birçok dosya var. Nihai N (örneğin, N = 4) dosyalarını ana dizine kopyalamak istiyorum. Bunu nasıl yapmalıyım?

cp ./<the final 4 files> ~/


1
Aşağıdaki tüm yanıtlarda, aralıklarını kullanarak komutların önüne "LC_ALL = ..." eklemeniz gerekebilir, böylece aralıkları sizin için doğru yerel ayarı kullanır (yerel ayarlar değişebilir ve karakterlerin sırası değişebilir) Örneğin, bazı yerel ayarlarda, [az] harflerin sıralandığı gibi "Z" dışındaki tüm alfabedeki harfleri içerebilir: aAbBcC .... zZ.Diğer varyasyonlar da vardır (vurgulu harfler ve hatta daha egzotik sıralamalar) Olağan bir seçim:LC_ALL=C
Olivier Dulac

Yanıtlar:


23

Bu bash / ksh93 / zsh dizileri ile kolayca yapılabilir:

a=(*)
cp -- "${a[@]: -4}" ~/

Bu, boşluklar, sekmeler, yeni satırlar veya diğer zor karakterler olsa bile gizli olmayan tüm dosya adları için geçerlidir (geçerli dizinde gizli olmayan en az 4 dosya olduğu varsayılarak bash).

Nasıl çalışır

  • a=(*)

    Bu, atüm dosya adlarına sahip bir dizi oluşturur . Bash tarafından döndürülen dosya adları alfabetik olarak sıralanır. (Bunun "dosya adına göre sıralanmıştır" ile kastettiğini varsayıyorum . )

  • ${a[@]: -4}

    Bu, dizinin son dört öğesini döndürür a(dizinin en az 4 öğe içermesi koşuluyla bash).

  • cp -- "${a[@]: -4}" ~/

    Bu, son dört dosya adını giriş dizininize kopyalar.

Aynı anda kopyalamak ve yeniden adlandırmak için

Bu, son dört dosyayı yalnızca ana dizine kopyalar ve aynı zamanda dizeyi a_kopyalanan dosyanın adının başına ekler:

a=(*)
for fname in "${a[@]: -4}"; do cp -- "$fname" ~/a_"$fname"; done

Farklı bir dizinden kopyalama ve yeniden adlandırma

a=(./some_dir/*)Bunun yerine kullanırsak a=(*), dizinin dosya adına bağlı sorunu var. Bir çözüm:

a=(./some_dir/*) 
for f in "${a[@]: -4}"; do cp "$f"  ~/a_"${f##*/}"; done

Başka bir çözüm, bir alt kabuk kullanmak ve alt kabuktaki cddizine kullanmaktır :

(cd ./some_dir && a=(*) && for f in "${a[@]: -4}"; do cp -- "$f" ~/a_"$f"; done) 

Alt kabuk tamamlandığında, kabuk bizi orijinal dizine döndürür.

Siparişin tutarlı olduğundan emin olmak

Soru, "dosya adına göre sıralanmış" dosyaları soruyor. Olivier Dulac yorumlarda dikkat çeken bu emir bir bölgeden diğerine değişecektir. Sonuçların makine ayarlarından bağımsız olarak sabitlenmesi önemliyse, dizi atanımlandığında yerel ayarı açıkça belirtmek en iyisidir . Örneğin:

LC_ALL=C a=(*)

Şu anda hangi yerel ayarda olduğunuzu bu localekomutu çalıştırarak öğrenebilirsiniz .


9

Kullanıyorsanız, istenen dosyaları seçen glob niteleyicileri listesini zshparantez içine alabilirsiniz . Senin durumunda, bu olurdu()

cp *(On[1,4]) ~/

Burada Ondosya adlarını alfabetik olarak tersine sıralar ve [1,4]sadece ilk 4 tanesini alır.

Bunu yalnızca düz dosyaları (dizinler, borular vb. Hariç) seçerek .ve adların düzgün şekilde başladığı dosyaları işlemek --için cpkomuta ekleyerek daha sağlam hale -getirebilirsiniz, bu nedenle:

cp -- *(.On[1,4]) ~

DGizli dosyaları (nokta dosyaları) da dikkate almak istiyorsanız niteleyiciyi ekleyin :

cp -- *(D.On[1,4]) ~

2
Veya: *([-4,-1]).
Stéphane Chazelas

2
@ StéphaneChazelas evet, gelecekteki okuyucular için normal yönde ( on) alfabetik olarak sıralamanın varsayılan davranış olduğunu açıklığa kavuşturalım , bu yüzden bunu belirtmeye gerek yoktur -ve sayıların önünde alttan dosya adlarını sayar.
15:15, jimmij

7

İşte son derece basit bash komutlarını kullanan bir çözüm.

find . -maxdepth 1 -type f | sort | tail -n 4 | while read -r file; do cp "$file" ~/; done

Açıklama:

find . -maxdepth 1 -type f

Geçerli dizindeki tüm dosyaları bulur.

sort

Alfabetik olarak sıralar.

tail -n 4

Yalnızca son 4 satırı göster.

while read -r file; do cp "$file" ~/; done

Copy komutunu uygulayan her satırın üzerinden döner.


6

Kabuk türünü hoş bulduğunuz sürece, şunları yapabilirsiniz:

set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
cp "$@" /path/to/target/dir

Bu, bashsunulan -specific dizi çözümüne çok benzer , ancak herhangi bir POSIX uyumlu kabuğa taşınabilir olmalıdır. Her iki yöntemle ilgili bazı notlar:

  1. cpArgümanlarınızı w / ile yönlendirmeniz cp --ya da .bir noktadan ya da /her ismin başına geçmeniz önemlidir . Bunu yapamazsanız, ilk argümanında seçenek olarak yorumlanabilen ve işlemin başarısız olmasına veya istenmeyen sonuçlara yol -açmasına neden olabilecek bir ön çizgi riskiyle karşı karşıya kalırsınız cp.

    • Geçerli dizin ile kaynak dizin olarak çalışsa bile bu kolayca yapılabilir ... set -- ./*veya array=(./*).
  2. Her iki yöntemi kullanırken, arg dizinizde kaldırmaya çalıştığınız kadar çok öğeye sahip olduğunuzdan emin olmak önemlidir - bunu burada bir matematik genişlemesi ile yapıyorum. shiftEn az 4 arg dizideki öğeleri olup olmadığını sadece olur - sadece vardiya uzakta sonra ve 4 bir fazla yapmak o ilk args ..

    • Yani bir set 1 2 3 4 5durumda 1uzaklaşacak, ama bir set 1 2 3durumda hiçbir şeyi değiştirmeyecek.
    • Örneğin: a=(1 2 3); echo "${a[@]: -4}"boş bir satır yazdırır.

Bir dizinden diğerine kopyalama yapıyorsanız, aşağıdakileri kullanabilirsiniz pax:

set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
pax -rws '|.*/|new_prefix|' "$@" /path/to/target/dir

... sedkopyalarken tüm dosya adlarına bir stil değişikliği uygular .


4

Yalnızca dosyalar varsa ve adları boşluk veya yeni satır içermiyorsa (ve değiştirmediyseniz $IFS) veya glob karakterler (veya bazı uygulamaları olan bazı yazdırılamayan karakterler) içermiyorsa lsve bununla başlamıyorsanız ., bunu yapabilirsiniz. :

cp -- $(ls | tail -n 4) ~/

Buna komut değiştirme denir ve gerçekten kullanışlıdır. tldp.org/LDP/abs/html/commandsub.html#CSPARENS
iyrin

Teşekkürler! İşe yarıyor. Hızlı bir a_şekilde TÜM dört dosyaya önek eklemek için uzak var mı ? Denedim echo "a_$(ls | tail -n 4)", ancak sadece ilk dosyayı öneriyor.
Sibbs Kumar

@SibbsGambling Benim yaklaşımım buna uygun değil, ancak John1024'ün cevabı buna kolayca adapte edilebilir.
Hauke ​​Laging

5
Genelde ayrıştırma lsçıktısı kötü form olarak kabul edilir, çünkü genellikle yalnızca boşluklar değil, aynı zamanda sekmeler, satırlar veya diğer geçerli ama "zor" karakterler içeren dosya adlarında da korkunç şekilde başarısız olur.
HBruijn

2
@HaukeLaging Şu anda bir sorun olmasa bile (dizinimde "karmaşık" firenamesim olmadığı için), 2 yıl içinde olabilirim ve aniden işler ayaklarımın üzerine düşer ...
glglgl

1

Bu, herhangi bir döngü kodu olmayan basit bir bash çözümüdür. Son 4 dosyayı geçerli dizinden hedefe kopyalayın:

$ find . -maxdepth 1 -type f |tail -n -4|xargs cp -t "$destdir"

1
Bunun find, dosyaları istenen sırada vermesini nasıl sağlıyorsunuz? Adlarında boşluk karakteri (yeni satırlar dahil) içeren dosyaların doğru şekilde işlenmesini nasıl sağlarsınız?
Kusalananda
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.