Один вкладыш с использованиемsed
:
sed -nf <(sed 's/$/p/' linenumberfile) contentfile
Чтобы сохранить первоначальный порядок в linenumberfile
, вы можете выполнить
sed -nf <(sed 's/$/p/' linenumberfile) contentfile | paste <(nl linenumberfile | sort -n -k 2,2) - | sort -n -k 1,1 | cut -f 3-
Пояснение:
sed 's/$/p/' linenumberfile
генерирует сценарий sed
, который печатает указанную строку. Затем сценарий передается в другойsed
(с -n
, чтобы подавить стандартную печать пространства шаблонов )для фактической печати. Поскольку sed
обрабатывает файл содержимого построчно, вывод будет в том же порядке, что и в файле содержимого. Обратите внимание, что это однопроходный -процесс , поэтому я ожидаю, что скорость будет приемлемой.
Чтобы ускорить процесс, можно изменить p
на {p;b}
и добавить q
в конец сгенерированного сценария sed
.
Чтобы сохранить порядок строк в файле номеров строк, nl
используется для добавления «номеров строк» в файл номеров строк. Итак, файл с номером строки
4
5
2
станет
1 4
2 5
3 2
В первом столбце записан исходный порядок в файле номеров строк.
Файл с «номера строк» затем sort
ed и paste
d на выходе sed
, чтобы сделать
3 2 content_of_line2
1 4 content_of_line4
2 5 content_of_line5
затем выполняется sort
редактирование с использованием 1-го столбца в качестве ключа, чтобы окончательно получить
1 4 content_of_line4
2 5 content_of_line5
3 2 content_of_line2
Наконец, cut
используется для удаления двух дополнительных столбцов.
Сравнительный анализ
Кажется, что sed
лучше всего подходит для нескольких строк, но perl
— это путь для 10000 строк, как указано в вопросе.
$ cat /proc/cpuinfo | grep -A 4 -m 1 processor
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 60
model name : Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz
$ wc -l linenumber
10 linenumber
$ wc -l content
8982457 content
$ file content
content: ASCII text
$ time bash -c "sed -nf <(sed 's/$/p/' linenumber) content > /dev/null"
real 0m0.791s
user 0m0.661s
sys 0m0.133s
$ time bash -c "awk 'FNR==NR { seen[$0]++ }; FNR!=NR && FNR in seen' linenumber content > /dev/null"
real 0m3.061s
user 0m2.908s
sys 0m0.152s
$ time bash -c "./ln.pl linenumber content > /dev/null"
real 0m1.706s
user 0m1.582s
sys 0m0.124s
$./genlinenumber.py 100 > linenumber
$ wc -l linenumber
100 linenumber
$ time bash -c "sed -nf <(sed 's/$/p/' linenumber) content > /dev/null"
real 0m3.326s
user 0m3.164s
sys 0m0.164s
$ time bash -c "awk 'FNR==NR { seen[$0]++ }; FNR!=NR && FNR in seen' linenumber content > /dev/null"
real 0m3.055s
user 0m2.890s
sys 0m0.164s
$ time bash -c "./ln.pl linenumber content > /dev/null"
real 0m1.769s
user 0m1.604s
sys 0m0.165s
Если требуется сохранить порядок строк, можно использовать команду после первой |
, так как время незначительно.
$./genlinenumber.py 10000 > linenumber
$ wc -l linenumber
10000 linenumber
$ time bash -c "./ln.pl linenumber content > extract"
real 0m1.933s
user 0m1.791s
sys 0m0.141s
$ time bash -c "paste <(nl linenumber | sort -n -k 2,2) extract | sort -n -k 1,1 | cut -f 3- > /dev/null"
real 0m0.018s
user 0m0.012s
sys 0m0.005s
$ tree
.
|-- file 1
| |-- file - 1 - A2.mkv
| `-- file - 1 - A2.nfo
|-- français
| `-- français - 2 -3.mkv
`-- tést
`-- tést - 2 - 2.mkv
3 directories, 4 files
$ LC_ALL=C find. -name '*[![:print:]]*'
./tést
./tést/tést - 2 - 2.mkv
./français
./français/français - 2 -3.mkv
При этом в качестве локали для команды find
устанавливается стандартная локаль POSIX. Класс символов print
содержит символы, которые являются частью классов символов alpha
, digit
, punct
, а также включает символ пробела. Это означает, что проверка -name '*[![:print:]]*'
будет истинной для любого имени файла, содержащего символ, который , а не в классе print
.
Если вы не хотите находить имена с различными другими пробелами (, вкладки и т. д. ),используйте [![:graph:][:space:]]
в качестве теста (единственная разница между print
и graph
заключается в том, что graph
не содержит символ пробела ).
Ответ Кусалананды также включает имена файлов с управляющими символами ASCII. Это может быть желательно, но в случае, если это не так, вот решение, основанное на решении Кусалананды, которое более точно отвечает на вопрос:
LC_ALL=C find. -name $'*[\x80-\xff]*'
Пример использования:
$ touch foo bár $'baz\x01'
$ ls
bár 'baz'$'\001' foo
$ LC_ALL=C find. -name $'*[\x80-\xff]*'
./b??r
$ LC_ALL=C find. -name $'*[\x80-\xff]*' | od -tx1z
0000000 2e 2f 62 c3 a1 72 0a >./b..r.<
0000007
Разница с тем, что вы пробовали, заключается в том, что здесь оболочка интерпретирует шестнадцатеричные escape-последовательности вместо того, чтобы оставить это find
. Кроме того, LC_ALL=C
необходим, вероятно, потому, что в противном случае .
в регулярном выражении или *
в глобах будут соответствовать этим байтам как части других символов.