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, a
tü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 cd
dizine 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 a
tanımlandığında yerel ayarı açıkça belirtmek en iyisidir . Örneğin:
LC_ALL=C a=(*)
Şu anda hangi yerel ayarda olduğunuzu bu locale
komutu çalıştırarak öğrenebilirsiniz .
Kullanıyorsanız, istenen dosyaları seçen glob niteleyicileri listesini zsh
parantez içine alabilirsiniz . Senin durumunda, bu olurdu()
cp *(On[1,4]) ~/
Burada On
dosya 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 cp
komuta ekleyerek daha sağlam hale -
getirebilirsiniz, bu nedenle:
cp -- *(.On[1,4]) ~
D
Gizli 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, bash
sunulan -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:
cp
Argü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. shift
En 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 5
durumda 1
uzaklaşacak, ama bir set 1 2 3
durumda 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
... sed
kopyalarken 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 ls
ve 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