grep -n | sort | sed | cut
( export LC_ALL=C
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
) <./F
Це повинно працювати досить швидко (деякі тестові випробування включені нижче) із введенням будь-якого розміру. Деякі примітки про те, як:
export LC_ALL=C
- Оскільки суть наступної операції полягає в тому, щоб отримати весь файл з
./F
накопиченим ./L
рядком в рядку з файлом lineno, єдині символи, про які нам дійсно потрібно буде турбуватися, - це [0-9]
цифри ASCII і :
двокрапка.
- З цієї причини легше турбуватися про те, щоб знайти ці 11 символів у наборі з 128 можливих, ніж це, якщо UTF-8 іншим чином залучений.
grep -n ''
- Це вставляє рядок
LINENO:
у голову кожного рядка в stdin - або <./F
.
sort -t: -nmk1,1 ./L -
sort
нехтує взагалі сортування вхідних файлів, а натомість (правильно) припускає, що вони -m
скасовуються та видаляє їх у -numerically
відсортованому порядку, ігноруючи в основному що-небудь, що перевищує будь-який можливий символ, -k1,1
що зустрічається -t:
двокрапкою.
- Хоча для цього може знадобитися деякий тимчасовий простір (залежно від того, наскільки далеко можуть відбуватися деякі послідовності) , це не зажадає багато в порівнянні з належним сортуванням, і це буде дуже швидко, оскільки воно включає нульове зворотнє відстеження.
sort
виведе єдиний потік, де будь-який лінобетон в ./L
буде негайно передувати відповідним рядкам у ./F
. ./L
Рядки завжди виходять першими, оскільки вони коротші.
sed /:/d\;n
- Якщо поточний рядок збігається з
/:/
двокрапкою, d
виберіть його з виводу. Ще, автоматично роздрукуйте поточний та зовнішній n
рядок.
- І так виводиться
sed
чорнослив лише на послідовні пари рядків, які не відповідають двокрапці та наступному рядку - або лише рядку від, а потім наступному.sort
./L
cut -sd: -f2-
cut
-s
підсилює з виводу ті вхідні рядки, які не містять принаймні одного з його -d:
рядків елімінатора - і таким чином ./L
рядки обрізаються повністю.
- Для тих ліній , які роблять їх перші
:
двокрапки -f
IELD це cut
далеко - і так йдуть всі grep
«вставляють LINENO років.
невеликий вхідний тест
seq 5 | sed -ne'2,3!w /tmp/L
s/.*/a-z &\& 0-9/p' >/tmp/F
... генерує 5 рядків зразкового введення. Потім...
( export LC_ALL=C; </tmp/F \
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
)| head - /tmp[FL]
... відбитки ...
==> standard input <==
a-z 1& 0-9
a-z 4& 0-9
a-z 5& 0-9
==> /tmp/F <==
a-z 1& 0-9
a-z 2& 0-9
a-z 3& 0-9
a-z 4& 0-9
a-z 5& 0-9
==> /tmp/L <==
1
4
5
більш тестові випробування
Я створив пару досить великих файлів:
seq 5000000 | tee /tmp/F |
sort -R | head -n1500000 |
sort -n >/tmp/L
... які помістили 5mil рядків у /tmp/F
та 1,5mil випадковим чином вибраних ліній у /tmp/L
. Я тоді:
time \
( export LC_ALL=C
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
) <./F |wc - l
Він надрукував:
1500000
grep -n '' \
0.82s user 0.05s system 73% cpu 1.185 total
sort -t: -nmk1,1 /tmp/L - \
0.92s user 0.11s system 86% cpu 1.185 total
sed /:/d\;n \
1.02s user 0.14s system 98% cpu 1.185 total
cut -sd: -f2- \
0.79s user 0.17s system 80% cpu 1.184 total
wc -l \
0.05s user 0.07s system 10% cpu 1.183 total
(Я додав туди косої риски)
Серед пропонованих тут рішень, це найшвидший з усіх, але один, коли він відповідає набору даних, згенерованих вище на моїй машині. З інших тільки один був близький до претендують на друге місце, і це meuh ось perl
тут .
Це аж ніяк не оригінальне пропоноване рішення - воно втратило третину часу його виконання завдяки порадам / натхненню, запропонованим іншими. Дивіться історію публікацій для повільніших рішень (але чому?) .
Крім того, варто відзначити, що деякі інші відповіді можуть бути набагато кращими, якби не архітектура з декількома процесорами моєї системи та одночасне виконання кожного з процесів у цьому конвеєрі. Всі вони працюють одночасно - кожен у своєму ядрі процесора - передаючи дані та виконуючи свою невелику частину цілого. Це досить круто.
але найшвидше рішення -
Але це не найшвидше рішення. Найшвидше рішення пропонується тут, руки вниз, це програма C . Я це назвав cselect
. Після копіювання його до буфера обміну X я скомпілював його так:
xsel -bo | cc -xc - -o cselect
Я тоді:
time \
./cselect /tmp/L /tmp/F |
wc -l
... і результати були ...
1500000
./cselect /tmp/L /tmp/F \
0.50s user 0.05s system 99% cpu 0.551 total
wc -l \
0.05s user 0.05s system 19% cpu 0.551 total