çok sayıda dosyayı doğru sırada bir araya getirin


23

Ben adlandırılır 15.000 hakkında dosyaları var file_1.pdb, file_2.pdb, yaparak sırayla bunlardan birkaç bin yaklaşık vb ben kutu kedi:

cat file_{1..2000}.pdb >> file_all.pdb

Ancak, bunu 15.000 dosya için yaparsam, hatayı alıyorum

-bash: /bin/cat: Argument list too long

Bu sorunun yaparak çözüldüğünü gördüm, find . -name xx -exec xxancak bu dosyaların birleştirildiği sırayı korumaz. Bunu nasıl başarabilirim?


3
Onuncu dosya adı nedir? (Veya tek basamaklı numaralı siparişten daha fazlasına sahip herhangi bir dosya.)
roaima

(Şimdi) bir dizinde bu dosyaları 15.000 var ve cat file_{1..15000}.pdbyapı benim için iyi çalışıyor.
roaima

11
limite ne bağlı olduğuna bağlıdır. getconf ARG_MAXsöylemeli.
ilkkachu

3
Sorunuzu "binlerce" veya "çok sayıda" dosya olarak değiştirmeyi düşünün. Sorunu benzer bir sorunu olan diğer insanlar için bulmayı kolaylaştırabilir.
msouth

Yanıtlar:


49

Kullanılması find, sortve xargs:

find . -maxdepth 1 -type f -name 'file_*.pdb' -print0 |
sort -zV |
xargs -0 cat >all.pdb

findKomut sonra dışarı onların pathnames yazdırır, tüm ilgili dosyaları bulur sorto doğru sırayla almak için bir "sürüm tür" yapar (biz gerekli olmazdı genişlik Dosya adlarında sayılar olsaydı sabit sıfır dolgulu -V). xargsbu sıralı yol adları listesini alır ve catbunlar üzerinde olabildiğince büyük gruplar halinde çalışır .

Dosya adları yeni satırlar ve boşluklar gibi garip karakterler içeriyor olsa bile bu işe yarayacaktır. Biz kullanmak -print0ile finddoğurup sortnul-sonlandırılmış tür adları ve sortkolları bu kullanarak -z. Bayrağı xargsile geçersiz sonlandırılmış isimleri de okur -0.

Sonucu, adı kalıpla eşleşmeyen bir dosyaya yazdığımı unutmayın file_*.pdb.


Yukarıdaki çözüm, bazı yardımcı programlar için standart olmayan bazı bayraklar kullanır. Bunlar, bu yardımcı programların GNU uygulaması ve en azından OpenBSD ve macOS uygulaması tarafından desteklenir.

Kullanılan standart olmayan bayraklar

  • -maxdepth 1, findyalnızca en üstteki dizini girin, ancak alt dizinleri girmeyin. POSIXly, kullanınfind . ! -name . -prune ...
  • -print0, findçıkış geçersiz sonlandırılmış yol adları yapmak için (bu POSIX tarafından kabul edildi, ancak reddedildi). Bunun -exec printf '%s\0' {} +yerine biri kullanılabilir .
  • -z, sortnul sonlandırılmış kayıtlar almak. POSIX denkliği yoktur.
  • -V, sortsıralamak için örneğin 200sonra 3. POSIX eşdeğeri yoktur, ancak dosya adlarının sabit bir öneki varsa dosya adının belirli bölümlerinde sayısal bir sıralama ile değiştirilebilir.
  • -0, xargsokunmuş sonlandırılmış kayıtlar yapmak. POSIX denkliği yoktur. POZİSYONEL olarak, dosya adlarını tanıdığı bir biçimde alıntılamak gerekir xargs.

Yol tanımları iyi huylu ise dizin yapısı düz (hayır alt) ise, ve, daha sonra bir haricinde bu bayrakların olmadan yapabilir -Vile sort.


1
Bunun için standart olmayan boş sonlandırmaya ihtiyacınız yoktur. Bu dosya adları son derece sıkıcıdır ve POSIX araçları tamamen işleyebilir.
Kevin

6
Bunu sorucunun şartnamesi ile printf ‘file_%d.pdb\0’ {1..15000} | xargs -0 cat, hatta Kevin'in noktasıyla daha da kısaca yazabilirsiniz echo file_{1..15000}.pdb | xargs cat. findSolüsyon çok daha havai bu o dosyalar için dosya sistemini aramak zorundadır çünkü var, ama bazı dosyalar var olmayabilir zaman daha yararlıdır.
kojiro

4
@Kevin, söylediğiniz şey doğru olsa da, daha genel koşullarda geçerli bir cevaba sahip olmak muhtemelen daha iyidir. Bu soruyu soran gelecek bin kişiden, bazılarının dosya adlarında boşluk veya herhangi bir şey olması muhtemeldir.
msouth

1
@chrylis Yönlendirme hiçbir zaman komutun bağımsız değişkenlerinin bir parçası değildir ve yönlendirilmekten xargsziyade cat(her catçağrı xargsstandart çıktıyı kullanır ). Eğer söylemiş xargs -0 sh -c 'cat >all.pdb'olsaydık, >>bunun yerine kullanmak mantıklı olurdu >, eğer bahsettiğiniz şey buysa.
Kusalananda

1
Çalışır gibi görünüyor sort -n -k1.6(orijinal, file_nnndosya adları veya sort -n -k1.5alt çizgi olmayanlar için).
Scott

14

İle zsh(bu {1..15000}operatörün geldiği yer):

autoload zargs # best in ~/.zshrc
zargs file_{1..15000}.pdb -- cat > file_all.pdb

Veya file_<digits>.pdbsayısal sırayla tüm dosyalar için:

zargs file_<->.pdb(n) -- cat > file_all.pdb

(burada <x-y>x ile y arasındaki ondalık sayılarla eşleşen bir glob operatörüdür. xNe yolursa olsun, ondalık sayı yoktur. extendedglob's [0-9]##veya kshglob' s +([0-9])(bir veya daha fazla basamak) ile eşdeğerdir ).

İle ksh93, yerleşik catkomutunu kullanarak ( yürütmeexecve() olmadığından sistem çağrısının bu sınırından etkilenmez ):

command /opt/ast/bin/cat file_{1..15000}.pdb > file_all.pdb

İle bash/ zsh/ ksh93(destek olan zshsitesindeki {x..y}ve bilgisi printfyerleşik):

printf '%s\n' file_{1..15000}.pdb | xargs cat > file_all.pdb

Bir GNU sisteminde veya uyumlu olarak şunları da kullanabilirsiniz seq:

seq -f 'file_%.17g.pdb' 15000 | xargs cat > file_all.pdb

İçin xargstabanlı çözümlere, özel bakım boşlukları, tek veya çift tırnak veya ters eğik çizgi içeren dosya adları için alınması gerekir.

Şunun için -It's a trickier filename - 12.pdbkullanın:

seq -f "\"./-It's a trickier filename - %.17g.pdb\"" 15000 |
  xargs cat > file_all.pdb

seq -f | xarg cat > En şık ve etkili bir çözümdür. (BENİM NACİZANE FİKRİME GÖRE).
Hastur

Daha hileli dosya adını kontrol edin ... belki '"./-It'\''s a trickier filename - %.17g.pdb"'?
Hastur

@ Hastur, ayy! Evet, teşekkürler, alternatif bir alıntı sözdizimiyle değiştirdim. Senin de işe yarayacaktı.
Stéphane Chazelas

11

Bir for döngüsü mümkün ve çok basit.

for i in file_{1..15000}.pdb; do cat $i >> file_all.pdb; done

Dezavantajı, catbirçok kez cehennemi çağırmanızdır . Ancak, işlerin nasıl yapılacağını tam olarak hatırlayamıyorsanız findve çağrı yükü durumunuzda çok kötü değilse, o zaman akılda tutmaya değer.


echo $i;Döngü gövdesinde genellikle bir "ilerleme göstergesi" olarak a ekliyorum
Rolf

3
seq 1 15000 | awk '{print "file_"$0".dat"}' | xargs cat > file_all.pdb

1
burada seq işini yapabilir ve seq -f file_%.10g.pdb 15000. Bunun seqstandart bir komut olmadığını unutmayın .
Stéphane Chazelas

Teşekkürler Stéphane - Bence seq -f bunu yapmanın harika bir yolu; bunu hatırlayacak.
LarryC

2

Öncül

Bu hataya yalnızca belirli ad biçimine sahip 15 bin dosya için girmemelisiniz [ 1 , 2 ] .

Bu genişletmeyi başka bir dizinden çalıştırıyorsanız ve her dosyaya yol eklemeniz gerekiyorsa, komutunuzun boyutu daha büyük olur ve elbette oluşabilir.

Çözüm bu dizinden komutu çalıştırın.

(cd That/Directory ; cat file_{1..2000}.pdb >> file_all.pdb )

En iyi çözüm Bunun yerine kötü tahmin ve eğer dosyaları olduğu dizinden çalıştırın ...
IMHO en iyi çözüm Stéphane Chazelas 'olanlar :

seq -f 'file_%.17g.pdb' 15000 | xargs cat > file_all.pdb

printf veya seq ile; sadece önbellek içindeki numaraları ile 15k dosyalarda test daha hızlı olanı (şu anda ve dosyaları aynı dizinden OP biri hariç).

Bazı kelimeler daha

Kabuk komut satırlarınıza daha uzun süre geçebilmelisiniz.
Komut satırınız 213914 karakter uzunluğunda ve 15003 kelime içeriyor
cat file_{1..15000}.pdb " > file_all.pdb" | wc

... her kelime için 8 bayt eklemek bile, 3.13.0 ARG_MAXçekirdeğinde veya biraz daha küçük 2088232'de "Gerçekte yapabileceğimiz maksimum komut uzunluğu" tarafından "kullan"xargs --show-limits

Sisteminizin çıktısına sisteminize bir göz atın

getconf ARG_MAX
xargs --show-limits

Tembellik rehberli çözüm

Bu gibi durumlarda, genellikle zaman etkili bir çözüm çıktığı için bloklarla çalışmayı tercih ederim.
Mantık (eğer varsa) 1 ... 1000 1001..2000 vs yazmak için çok tembelim ...
Yani bir senaryoyu benim için yapmasını istiyorum.
Yalnızca çıktı doğruluğunu kontrol ettikten sonra bir betiğe yönlendiriyorum.

... ama Tembellik bir ruh halidir .
Alerjim olduğu için xargs(gerçekten xargsburada kullanmalıydım) ve nasıl kullanılacağını kontrol etmek istemiyorum, aşağıdaki örneklerde olduğu gibi tekerleği yeniden icat etmek için zamanında bitiriyorum (tl; dr).

Dosya adları kontrol edildiğinden (boşluk yok, yeni satırlar ...) aşağıdaki komut dosyası gibi bir şeyle kolayca gidebileceğinizi unutmayın.

tl; Dr.

Sürüm 1: isteğe bağlı parametre olarak 1. dosya numarası, son, blok boyutu, çıktı dosyası

#!/bin/bash
StartN=${1:-1}          # First file number
EndN=${2:-15000}        # Last file number
BlockN=${3:-100}        # files in a Block 
OutFile=${4:-"all.pdb"} # Output file name

CurrentStart=$StartN 
for i in $(seq $StartN $BlockN $EndN)
do 
  CurrentEnd=$i ;  
    cat $(seq -f file_%.17g.pdb $CurrentStart $CurrentEnd)  >> $OutFile;
  CurrentStart=$(( CurrentEnd + 1 )) 
done
# Here you may need to do a last iteration for the part cut from seq
[[ $EndN -ge $CurrentStart ]] && 
    cat $(seq -f file_%.17g.pdb $CurrentStart $EndN)  >> $OutFile;

Versiyon 2

Genişleme için bash çağrılıyor (testlerimde biraz daha yavaş ~% 20).

#!/bin/bash
StartN=${1:-1}          # First file number
EndN=${2:-15000}        # Last file number
BlockN=${3:-100}        # files in a Block 
OutFile=${4:-"all.pdb"} # Output file name

CurrentStart=$StartN 
for i in $(seq $StartN $BlockN $EndN)
do 
  CurrentEnd=$i ;
    echo  cat file_{$CurrentStart..$CurrentEnd}.pdb | /bin/bash  >> $OutFile;
  CurrentStart=$(( CurrentEnd + 1 )) 
done
# Here you may need to do a last iteration for the part cut from seq
[[ $EndN -ge $CurrentStart ]] && 
    echo  cat file_{$CurrentStart..$EndN}.pdb | /bin/bash  >> $OutFile;

Tabii ki ileri gidip seq [ 3 ] 'den (coreutils'ten) tamamen kurtulabilir ve doğrudan bash değişkenleriyle çalışabilir veya python kullanabilir veya bunu yapmak için ac programını derleyebilirsiniz [ 4 ] ...


Bunun %gkısa olduğunu unutmayın %.6g. Örneğin 1.000.000'u 1e + 06 olarak temsil eder.
Stéphane Chazelas

Gerçekten tembel insanlar xargs, zsh zargsya da ksh93's gibi E2BIG sınırlaması üzerinde çalışmak için tasarlanmış araçları kullanırlar command -x.
Stéphane Chazelas

seqbir bash yerleşik değildir, GNU coreutils'in bir komutudur. seq -f %g 1000000 1000000coreutils'in en son sürümünde bile 1e + 06 çıktılar.
Stéphane Chazelas

@ StéphaneChazelas Tembellik bir ruh hali. Söylemek garip ama görebildiğimde (ve seri bir komutun çıkışını görsel olarak kontrol ettiğimde) daha rahat hissediyorum ve ancak daha sonra yürütmeye yönlendiriyorum. Bu yapı bana daha az düşünmemi sağlıyor xarg... ama bunun kişisel olduğunu ve belki de sadece benimle ilgili olduğunu anlıyorum.
Hastur

@ StéphaneChazelas Yakaladım, doğru ... Sabit. Teşekkürler. Ben sadece OP tarafından verilen 15k dosyaları ile test, benim kötü.
Hastur

0

Bunu yapmanın başka bir yolu da

(cat file_{1..499}.pdb; cat file_{500..999}.pdb; cat file_{1000..1499}.pdb; cat file_{1500..2000}.pdb) >> file_all.pdb
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.