Это работает:
find. -execdir sh \;
# Create some sample directories
$ for i in {1..4}; do mkdir -p "/tmp/example/$i/foo" ; done
# spawn
$ find -s /tmp/example -name foo -execdir sh \;
sh-3.2$ pwd
/private/tmp/example/1
sh-3.2$ exit
sh-3.2$ pwd
/private/tmp/example/2
sh-3.2$ exit
sh-3.2$ pwd
/private/tmp/example/3
sh-3.2$ exit
sh-3.2$ pwd
/private/tmp/example/4
sh-3.2$ exit
$
Соответствующая информация на справочной странице и/или другая информация:
-s
:Заставить find перемещаться по файловым иерархиям в лексикографическом порядке, т. е. в алфавитном порядке внутри каждого каталога. Это не критично для решения, но делает порядок оболочек предсказуемым.
/tmp/example
:Аргумент пути. Утилита find рекурсивно спускается по дереву каталогов для каждого указанного пути.
-name pattern
:Соответствует файлам, если последний компонент проверяемого пути соответствует шаблону. Специальные символы соответствия шаблону оболочки(['',
]'', *'', and
?'' )могут использоваться как часть шаблона.
-execdir utility \;
:Первичный каталог -execdir идентичен первичному каталогу -exec, за исключением того, что утилита будет выполняться из каталога, содержащего текущий файл.
sh
:Запуск sh
в этом контексте запускает интерактивную оболочку из каталога, содержащего текущий файл.
Предположим, у вас 48 ядер, 500 ГБ свободной оперативной памяти, а файл состоит из 100 млн строк и помещается в памяти.
Если вы используете обычную сортировку, она будет довольно медленной:
$ time sort bigfile > bigfile.sort
real 4m48.664s
user 21m15.259s
sys 0m42.184s
Вы можете сделать это немного быстрее, игнорируя свой язык:
$ export LC_ALL=C
$ time sort bigfile > bigfile.sort
real 1m51.957s
user 6m2.053s
sys 0m42.524s
Вы можете сделать это быстрее, указав сортировке использовать больше ядер:
$ export LC_ALL=C
$ time sort --parallel=48 bigfile > bigfile.sort
real 1m39.977s
user 15m32.202s
sys 1m1.336s
Вы также можете попробовать выделить sort больше оперативной памяти (это не поможет, если sort уже достаточно памяти):
$ export LC_ALL=C
$ time sort --buffer-size=80% --parallel=48 bigfile > bigfile.sort
real 1m39.779s
user 14m31.033s
sys 1m0.304s
Но похоже, что sort действительно нравится делать много однопоточных операций. Вы можете заставить его распараллелить больше с помощью:
$ merge() {
if [ $1 -le 1 ] ; then
parallel -Xj1 -n2 --dr 'sort -m <({=uq=}) | mbuffer -m 30M;'
else
parallel -Xj1 -n2 --dr 'sort -m <({=uq=}) | mbuffer -m 30M;' |
merge $(( $1/2 ));
fi
}
# Generate commands that will read blocks of bigfile and sort those
# This only builds the command - it does not run anything
$ parallel --pipepart -a bigfile --block -1 --dr -vv sort |
# Merge these commands 2 by 2 until only one is left
# This only builds the command - it does not run anything
merge $(parallel --number-of-threads) |
# Execute the command
# This runs the command built in the previous step
bash > bigfile.sort
real 0m30.906s
user 0m21.963s
sys 0m28.870s
Он разбивает файл на 48 блоков на лету (по одному блоку на ядро ), сортирует эти блоки параллельно. Затем мы делаем слияние пары из них. Затем мы делаем слияние пары из них. Затем мы делаем слияние пары из них. Затем мы делаем слияние пары из них. Затем мы делаем слияние пары из них. И так далее, пока у нас не останется только один вход. Все это делается параллельно, когда это возможно.
Для файла размером 100 ГБ с 4 G строками тайминги равны:
$ LC_ALL=C time sort --parallel=48 -S 80% --compress-program pzstd bigfile >/dev/null
real 77m22.255s
$ LC_ALL=C time parsort bigfile >/dev/null
649.49user 727.04system 18:10.37elapsed 126%CPU (0avgtext+0avgdata 32896maxresident)k
Таким образом, использование распараллеливания ускоряет примерно в 4 раза.
Чтобы упростить использование, я превратил его в небольшой инструмент :parsort
, который теперь является частью GNU Parallel.
Он также поддерживает sort
параметры и чтение со стандартного ввода(parsort -k2rn < bigfile
).