С GNU sort
и оболочкой, в которую встроен printf
(в настоящее время все POSIX-подобные, за исключением некоторых вариантов pdksh
):
printf '%s\0' * | sort -u --files0-from=- > output
Теперь проблема заключается в том, что, поскольку два компонента этого конвейера выполняются одновременно и независимо, к тому времени, когда левый расширяет глобус *
, правый может уже создали файл output
, который может вызвать проблемы (возможно, не с -u
здесь), поскольку output
будет и входным, и выходным файлом, так что вы можете хотите, чтобы вывод перешел в другой каталог (например, > ../output
), или убедитесь, что glob не соответствует выходному файлу.
Другой способ обратиться к этому экземпляру — написать его:
printf '%s\0' * | sort -u --files0-from=- -o output
Таким образом, он сортирует
открывает выход
для записи и (в моих тестах) Не делайте этого до того, как он получит полный список файлов (так долго после того, как глобус был расширен). Это также позволит избежать затирания вывода
, если ни один из входных файлов не читается.
Другой способ записать это с помощью zsh
или bash
sort -u --files0-from=<(printf '%s\0' *) -o output
Это использование подстановки процесса (где <(...)
заменяется путем к файлу, который ссылается к считывающему концу канала printf
для записи). Эта функция происходит от ksh
, но ksh
настаивает на том, чтобы сделать расширение <(...)
отдельным аргументом команды, поэтому вы не можете использовать это с опцией --option=<(...)
синтаксис. Это будет работать с этим синтаксисом:
sort -u --files0-from <(printf '%s\0' *) -o output
Обратите внимание, что вы увидите разницу с подходами, которые передают вывод cat
в файлы в случаях, когда есть файлы, которые не заканчиваются новой строкой. символ:
$ printf a > a
$ printf b > b
$ printf '%s\0' a b | sort -u --files0-from=-
a
b
$ printf '%s\0' a b | xargs -r0 cat | sort -u
ab
Также обратите внимание, что sort
сортирует с использованием алгоритма сопоставления в локали (strcollate()
), а sort -u
сообщает об одном из каждого набора строк, которые сортируются одинаково по этому алгоритму, а не уникальные строки на уровне байтов. Если вы заботитесь только об уникальности строк на уровне байтов и не слишком заботитесь о порядке их сортировки, вы можете захотеть исправить языковой стандарт на C, где сортировка основана на байтовых значениях (memcmp( )
; это, вероятно, значительно ускорит процесс):
printf '%s\0' * | LC_ALL=C sort -u --files0-from=- -o output
Чтобы попытаться объяснить более буквально, чем великий теоретический ответ meuh -, как вы, вероятно, знаете, существует ряд файловых дескрипторов по умолчанию:
0
означает стандартный ввод 1
означает стандартный вывод 2
означает stderr Ваша команда делает следующее:
3>all
открыть новый файловый дескриптор, указывающий на файлall
1> >(tee out >&3)
перенаправить стандартный вывод(1
)на файловый дескриптор, открытый и возвращенный командой tee, как объяснил meuh tee out >&3
перенаправляет свой ввод (, в данном случае стандартный вывод из вашего скрипта )в файл с именем out, и везде, где файловый дескриптор 3
указывает на (, в этом случае файл all)2> >(tee err >&3)
перенаправить stderr(2
)на дескриптор файла, открытый и возвращенный командой tee, как объяснил meuh tee err >&3
перенаправляет свой ввод (, в данном случае stderr из вашего скрипта )в файл с именем err,и везде, где дескриптор файла 3
указывает на (, в этом случае файл все)Судя по вашему комментарию, я думаю, что вас смущает то, что вы ожидаете, что вам нужно будет использовать >>
в качестве оператора перенаправления, если вы хотите добавить вывод в файл.
Здесь дело обстоит иначе, так как все, что вы буквально делаете, это подключаете и stdout, и stderr к файловому дескриптору, который указывает на файл all.
Эффект тот же, что и при выполнении:
./my.sh > all 2>&1
Что сначала перенаправляет stdout в файл all, а затем перенаправляет stderr туда, куда указывает stdout.