кішка дуже велика кількість файлів разом у правильному порядку


23

У мене є близько 15 000 файлів, які названі file_1.pdbі file_2.pdbт.д.

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

Однак якщо я це роблю для 15000 файлів, я отримую помилку

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

Я бачив, як цю проблему вирішують, find . -name xx -exec xxале це не збереже порядок з'єднання файлів. Як я можу цього досягти?


3
Як називається десятий файл? (Або будь-який файл із впорядкуванням, що має більше цифр.)
roaima

У мене (зараз) 15000 цих файлів у каталозі, і ваша cat file_{1..15000}.pdbконструкція працює для мене добре.
roaima

11
від системи залежить, яка межа. getconf ARG_MAXповинен сказати.
ilkkachu

3
Подумайте про те, щоб змінити своє запитання на "тисячі" або "дуже велику кількість" файлів. Може полегшити пошук питання для інших людей із подібною проблемою.
msouth

Відповіді:


49

Використання find, sortі xargs:

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

findКоманда знаходить всі необхідні файли, а потім виводить їх імена шляхів до , sortщо робить «версію свого роду» , щоб отримати їх в правильному порядку (якщо цифри в іменах файлів були заповнені нулями до фіксованої ширини , ми потребували б не -V). xargsприймає цей список відсортованих імен шляхів і виконує catїх якомога більшими партіями.

Це має спрацювати, навіть якщо файли містять дивні символи, такі як нові рядки та пробіли. Ми використовуємо -print0з , findщоб дати sortNUL-перервані імена сортувати, і sortручки їх використання -z. xargsтеж читає скасовані назви імена зі своїм -0прапором.

Зауважте, що я записую результат у файл, ім'я якого не відповідає шаблону file_*.pdb.


Вищевказане рішення використовує деякі нестандартні прапори для деяких утиліт. Вони підтримуються реалізацією цих утиліт GNU і принаймні OpenBSD та реалізацією macOS.

Використовуються нестандартні прапори

  • -maxdepth 1, щоб внести findлише найпопулярніший каталог, але без підкаталогів. POSIXly, використовуватиfind . ! -name . -prune ...
  • -print0, щоб зробити findвихідні нульові імена траєкторій (це було розглянуто POSIX, але відхилено). Можна -exec printf '%s\0' {} +замість цього використовувати .
  • -z, зробити sortприйняття скасованих записів. Не існує еквівалентності POSIX.
  • -V, щоб зробити sortсортування, наприклад, 200після 3. Не існує еквівалентності POSIX, але його можна замінити числовим сортуванням на конкретних частинах імені файлу, якщо у назви файлів є фіксований префікс.
  • -0, зробити xargsчитання записів, скасованих за нуль. Не існує еквівалентності POSIX. POSIX, потрібно було б цитувати імена файлів у форматі, розпізнаваному xargs.

Якщо імена шляхів добре поводяться і якщо структура каталогу рівна (немає підкаталогів), тоді можна зробити без цих прапорів, за винятком -Vс sort.


1
Для цього вам не потрібно нестандартне нульове завершення. Ці назви файлів надзвичайно нудні, і інструменти POSIX цілком здатні працювати з ними.
Кевін

6
Ви також можете написати це більш стисло із зазначенням Аскер в якості printf ‘file_%d.pdb\0’ {1..15000} | xargs -0 cat, або навіть з точкою Кевіна, echo file_{1..15000}.pdb | xargs cat. findРішення має значно більш накладні витрати , так як він повинен шукати файлову систему для цих файлів, але це більш корисно , коли деякі файли можуть не існувати.
kojiro

4
@Kevin, хоча те, що ви говорите, є істинним, але, можливо, краще мати відповідь, яка застосовується в більш загальних обставинах. З наступних тисяч людей, які мають це запитання, ймовірно, що деякі з них матимуть пробіли чи що-небудь у своїх назвах файлів.
msouth

1
@chrylis Перенаправлення ніколи не є частиною аргументів команди, і це xargsшвидше, ніж catте, що перенаправляється (кожне catвиклик використовуватиме xargsстандартний вихід). Якби ми сказали, xargs -0 sh -c 'cat >all.pdb'тоді це мало б сенс використовувати >>замість цього >, якщо на це ти натякаєш.
Кусалаланда

1
Схоже, sort -n -k1.6це спрацювало (для оригіналу, file_nnnназви файлів файлів або sort -n -k1.5для тих, хто не підкреслює).
Скотт

14

З zsh(звідки {1..15000}походить цей оператор):

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

Або для всіх file_<digits>.pdbфайлів у цифровому порядку:

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

(де <x-y>глобальний оператор, який відповідає десятковим числам x до y. Без xні y, це будь-яке десяткове число. Еквівалентно extendedglob's [0-9]##або kshglob' s +([0-9])(одна чи більше цифр)).

З ksh93, використовуючи вбудовану catкоманду (так що не впливає цей ліміт execve()системного виклику, оскільки немає виконання ):

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

З bash/ zsh/ ksh93(що підтримка zsh«s {x..y}і мають printfвбудований):

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

У системі GNU або сумісній ви також можете використовувати seq:

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

Для xargsрішень, заснованих на основі, слід особливо уважно ставитись до імен файлів, які містять пробіли, одинарні або подвійні лапки або зворотні риски.

Як і для -It's a trickier filename - 12.pdbвикористання:

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

seq -f | xarg cat > Самий елегантне і ефективне рішення. (ІМХО).
Гастур

Перевірте складніше ім'я файлу ... можливо '"./-It'\''s a trickier filename - %.17g.pdb"'?
Гастур

@Hastur, ой! Так, дякую, я змінив його на альтернативний синтаксис цитування. Ваші також працюватимуть.
Стефан Шазелас

11

Цикл для циклу можливий і дуже простий.

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

Мінус полягає в тому, що ви catбагато разів викликаєте пекло. Але якщо ви не можете пам’ятати, як саме робити речі, findа виклик накладних витрат не надто поганий у вашій ситуації, то це варто пам’ятати.


Я часто додаю echo $i;в тіло циклу як "показник прогресу"
Рольф

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

1
тут можна виконати роботу seq, а тут можна виконати роботу seq -f file_%.10g.pdb 15000. Зверніть увагу, що seqце не стандартна команда.
Стефан Шазелас

Спасибі Стефане - я думаю seq -f , що це прекрасний спосіб зробити це; пам’ятатиму це.
LarryC

2

Приміщення

Ви не повинні зазнавати цієї помилки лише для 15-ти файлів із певним форматом імен [ 1 , 2 ] .

Якщо ви запускаєте це розширення з іншого каталогу і вам потрібно додати шлях до кожного файлу, розмір вашої команди буде більшим, і, звичайно, це може відбутися.

Рішення запустіть команду з цього каталогу.

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

Найкраще рішення Якщо натомість я здогадався погано, і ви запускаєте його з каталогу, в якому знаходяться файли ...
ІМХО найкращим рішенням є стифани Шазели :

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

з printf або seq; протестований на 15-ти файлах, лише їх кількість всередині попереднього кешування, це навіть більш швидкий (в даний час, за винятком OP-одного з тієї ж директорії, в якій є файли).

Деякі слова більше

Ви повинні мати можливість переходити до командних рядків оболонки довше.
Ваш командний рядок має 213914 символів і містить 15003 слова
cat file_{1..15000}.pdb " > file_all.pdb" | wc

... навіть додавання 8 байтів для кожного слова становить 333 938 байт (0,3 М) набагато нижче від 2097142 (2,1 М), про який повідомляється ARG_MAXв ядрі 3.13.0, або трохи менший 2088232 повідомляється як "Максимальна довжина команди, яку ми могли насправді використання " відxargs --show-limits

Погляньте на вашу систему на вихід

getconf ARG_MAX
xargs --show-limits

Лінь керований рішення

У таких випадках я вважаю за краще працювати з блоками навіть тому, що зазвичай виходить ефективне за часом рішення.
Логіка (якщо така є) - я дуже лінивий писати 1 ... 1000 1001..2000 і т. Д. І т.д. ...
Тож я прошу сценарій зробити це для мене.
Тільки після того, як я перевірив правильність виводу, я перенаправляю його на сценарій.

... але лінь - це стан душі .
Так як у мене алергія наxargs страждаю (я справді мав би xargsтут використовуватись ) і не хочу перевіряти, як це використовувати, я закінчую винахідлення колеса заново, як у наведених нижче прикладах (tl; dr).

Зауважте, що оскільки імена файлів керовані (немає пробілів, нових рядків ...), ви можете легко перейти з чимось на зразок сценарію нижче.

тл; д-р

Версія 1: передайте як необов'язковий параметр номер 1-го файлу, останній, розмір блоку, вихідний файл

#!/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;

Версія 2

Виклик bash для розширення (трохи повільніше в моїх тестах ~ 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;

Звичайно, ви можете піти вперед і повністю позбутися seq [ 3 ] (від coreutils) і працювати безпосередньо зі змінними в bash, або використовувати python, або компілювати програму змінного струму для цього [ 4 ] ...


Зауважте, що %gце короткий для %.6g. Наприклад, це буде 1 000 000 як 1e + 06.
Стефан Шазелас

Дійсно ліниві користуються інструментами, розробленими для вирішення такого обмеження, як xargs, наприклад , zsh zargsабо ksh93s command -x.
Стефан Шазелас

seqце не bash вбудований, це команда GNU coreutils. seq -f %g 1000000 1000000виводить 1e + 06 навіть в останній версії coreutils.
Стефан Шазелас

@ StéphaneChazelas Лінь - це стан душі. Як не дивно сказати, але я відчуваю себе затишніше, коли бачу (і візуально перевіряю вихід серіалізованої команди) і лише потім переспрямовую на виконання. Ця конструкція дає мені думати менше, ніж xarg... але я розумію, що це особисте і, можливо, пов'язане лише зі мною.
Гастур

@ StéphaneChazelas Gotcha, right ... Виправлено. Спасибі. Я перевіряв лише 15-ти файли, надані ОП, моє погано.
Гастур

0

Ще один спосіб зробити це міг бути

(cat file_{1..499}.pdb; cat file_{500..999}.pdb; cat file_{1000..1499}.pdb; cat file_{1500..2000}.pdb) >> file_all.pdb
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.