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> ~/
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> ~/
Yanıtlar:
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).
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.
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
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.
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 .
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]) ~
*([-4,-1]).
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.
İş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.
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:
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.
set -- ./*veya array=(./*).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 ..
set 1 2 3 4 5durumda 1uzaklaşacak, ama bir set 1 2 3durumda hiçbir şeyi değiştirmeyecek.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 .
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) ~/
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.
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.
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"
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?
LC_ALL=C