Saya memiliki sekitar 15.000 file yang diberi nama file_1.pdb
, file_2.pdb
, dll saya bisa kucing sekitar beberapa ribu dari ini dalam rangka dengan melakukan:
cat file_{1..2000}.pdb >> file_all.pdb
Namun, jika saya melakukan ini untuk 15.000 file, saya mendapatkan kesalahan
-bash: /bin/cat: Argument list too long
Saya telah melihat masalah ini diselesaikan dengan melakukan find . -name xx -exec xx
tetapi ini tidak akan mempertahankan urutan yang menggabungkan file. Bagaimana saya bisa mencapai ini?
files
find
cat
brace-expansion
sodiumnitrate
sumber
sumber
cat file_{1..15000}.pdb
konstruk Anda berfungsi dengan baik untuk saya.getconf ARG_MAX
harus memberi tahu.Jawaban:
Menggunakan
find
,sort
danxargs
:The
find
perintah menemukan semua file yang relevan, kemudian mencetak nama path mereka keluar untuksort
yang melakukan "versi semacam" untuk mendapatkan mereka dalam urutan yang benar (jika nomor di nama file telah nol-diisi dengan lebar tetap kita tidak akan diperlukan-V
).xargs
mengambil daftar nama jalur yang diurutkan ini dan menjalankannyacat
dalam jumlah yang sebanyak mungkin.Ini akan berfungsi bahkan jika nama file berisi karakter aneh seperti baris baru dan spasi. Kami menggunakan
-print0
denganfind
untuk memberikansort
nama yang diakhiri nul untuk mengurutkan, dansort
menangani ini menggunakan-z
.xargs
juga membaca nama yang diakhiri dengan-0
bendera.Perhatikan bahwa saya sedang menulis hasilnya ke file yang namanya tidak cocok dengan pola
file_*.pdb
.Solusi di atas menggunakan beberapa flag non-standar untuk beberapa utilitas. Ini didukung oleh implementasi GNU dari utilitas ini dan setidaknya oleh OpenBSD dan implementasi macOS.
Bendera non-standar yang digunakan adalah
-maxdepth 1
, untuk membuatfind
hanya memasuki direktori paling atas tetapi tidak ada subdirektori. POSIXly, gunakanfind . ! -name . -prune ...
-print0
, untuk membuatfind
keluaran nama jalur yang diakhiri nul (ini dianggap oleh POSIX tetapi ditolak). Orang bisa menggunakan-exec printf '%s\0' {} +
sebagai gantinya.-z
, untuk membuatsort
catatan nul-terminated. Tidak ada kesetaraan POSIX.-V
, untuk membuatsort
semacam mis200
setelah3
. Tidak ada kesetaraan POSIX, tetapi bisa diganti dengan semacam angka pada bagian-bagian tertentu dari nama file jika nama file memiliki awalan tetap.-0
, untuk membuatxargs
catatan yang diakhiri nul. Tidak ada kesetaraan POSIX. POSIXly, orang perlu mengutip nama file dalam format yang dikenali olehxargs
.Jika nama path berperilaku baik, dan jika struktur direktori datar (tanpa subdirektori), maka seseorang dapat melakukannya tanpa flag-flag ini, kecuali
-V
dengansort
.sumber
printf ‘file_%d.pdb\0’ {1..15000} | xargs -0 cat
, atau bahkan dengan poin Kevinecho file_{1..15000}.pdb | xargs cat
,. Thefind
solusi memiliki jauh lebih banyak overhead karena harus mencari sistem file untuk file-file, tetapi lebih berguna ketika beberapa file mungkin tidak ada.xargs
bukancat
yang diarahkan (setiapcat
doa akan menggunakanxargs
output standar). Jika kami mengatakannyaxargs -0 sh -c 'cat >all.pdb'
maka masuk akal untuk menggunakannya>>
sebagai ganti>
, jika itu yang Anda isyaratkan.sort -n -k1.6
akan berfungsi (untuk yang asli,file_nnn
nama file, atausort -n -k1.5
yang tanpa garis bawah).Dengan
zsh
( dari mana{1..15000}
operator itu berasal):Atau untuk semua
file_<digits>.pdb
file dalam urutan numerik:(di mana
<x-y>
adalah operator glob yang pertandingan pada angka desimal x ke y. Dengan tidak adax
atauy
, itu nomor desimal. Setara denganextendedglob
's[0-9]##
ataukshglob
' s+([0-9])
(satu atau lebih digit)).Dengan
ksh93
, menggunakancat
perintah bawaannya (jadi tidak terpengaruh oleh batasexecve()
pemanggilan sistem karena tidak ada eksekusi ):Dengan
bash
/zsh
/ksh93
(yang supportzsh
's{x..y}
dan memilikiprintf
builtin):Pada sistem GNU atau yang kompatibel, Anda juga dapat menggunakan
seq
:Untuk
xargs
solusi berbasis, perawatan khusus harus diambil untuk nama file yang berisi tanda kutip kosong, tunggal atau ganda atau garis miring terbalik.Seperti untuk
-It's a trickier filename - 12.pdb
, gunakan:sumber
seq -f | xarg cat >
adalah solusi yang paling elegan, dan efektif. (MENURUT OPINI SAYA).'"./-It'\''s a trickier filename - %.17g.pdb"'
?A for loop dimungkinkan, dan sangat sederhana.
Kelemahannya adalah Anda sering melakukan
cat
hal yang sama. Tetapi jika Anda tidak dapat mengingat dengan tepat bagaimana melakukan hal-hal denganfind
dan biaya overhead doa tidak terlalu buruk dalam situasi Anda, maka ada baiknya diingat.sumber
echo $i;
dalam tubuh loop sebagai "indikator kemajuan"sumber
seq -f file_%.10g.pdb 15000
. Perhatikan bahwaseq
ini bukan perintah standar.seq -f
ini cara yang bagus untuk melakukan ini; akan ingat itu.Premis
Anda seharusnya tidak melakukan kesalahan itu hanya untuk file 15k dengan format nama spesifik [ 1 , 2 ] .
Jika Anda menjalankan ekspansi itu dari direktori lain dan Anda harus menambahkan path ke setiap file, ukuran perintah Anda akan lebih besar, dan tentu saja itu bisa terjadi.
Solusi menjalankan perintah dari direktori itu.
Solusi Terbaik Jika sebaliknya saya menduga buruk dan Anda menjalankannya dari direktori di mana file-file tersebut ...
IMHO solusi terbaik adalah yang Stéphane Chazelas ' :
dengan printf atau seq; diuji pada file 15k dengan hanya nomor mereka di dalam pra-cache itu bahkan yang lebih cepat (saat ini dan kecuali OP satu dari direktori yang sama di mana file tersebut).
Beberapa kata lagi
Anda harus bisa melewati baris perintah shell Anda lebih lama.
Baris perintah Anda adalah 213914 karakter dan berisi 15003 kata
cat file_{1..15000}.pdb " > file_all.pdb" | wc
... bahkan menambahkan 8 byte untuk setiap kata adalah 333 938 byte (0,3M) jauh di bawah 2097142 (2,1M) yang dilaporkan oleh
ARG_MAX
kernel 3.13.0 atau 2088232 yang sedikit lebih kecil dilaporkan sebagai "Panjang perintah maksimum yang sebenarnya bisa kita dapat gunakan " olehxargs --show-limits
Coba lihat pada sistem Anda untuk output dari
Solusi yang dipandu kemalasan
Dalam kasus seperti ini saya lebih suka bekerja dengan balok bahkan karena biasanya keluar solusi waktu yang efisien.
Logikanya (jika ada) adalah saya terlalu malas untuk menulis 1 ... 1000 1001..2000 dll ...
Jadi saya meminta script untuk melakukannya untuk saya.
Hanya setelah saya memeriksa hasilnya sudah benar saya mengarahkan ulang ke skrip.
... tapi Kemalasan adalah kondisi pikiran .
Karena saya alergi
xargs
(saya benar-benar seharusnya menggunakannya dixargs
sini) dan saya tidak ingin memeriksa cara menggunakannya, saya segera selesai untuk menemukan kembali roda seperti pada contoh di bawah ini (tl; dr).Perhatikan bahwa karena nama file dikontrol (tanpa spasi, baris baru ...) Anda dapat dengan mudah menggunakan sesuatu seperti skrip di bawah ini.
tl; dr
Versi 1: lulus sebagai parameter opsional nomor file 1, yang terakhir, ukuran blok, file output
Versi 2
Memanggil bash untuk ekspansi (sedikit lebih lambat dalam pengujian saya ~ 20%).
Tentu saja Anda dapat maju dan menyingkirkan
seq
[ 3 ] (dari coreutils) dan bekerja secara langsung dengan variabel dalam bash, atau menggunakan python, atau kompilasi program ac untuk melakukannya [ 4 ] ...sumber
%g
kependekan dari%.6g
. Ini akan mewakili 1.000.000 sebagai 1e + 06 misalnya.xargs
, zsh'szargs
atauksh93
'scommand -x
.seq
bukan bash builtin, ini adalah perintah dari GNU coreutils.seq -f %g 1000000 1000000
menghasilkan 1e + 06 bahkan dalam versi terbaru dari coreutils.xarg
... tapi saya mengerti itu bersifat pribadi dan mungkin hanya terkait dengan saya.Cara lain untuk melakukannya bisa jadi
sumber