Как разделить строки в текстовом файле в две последовательных строки в новом файле?

У меня есть a find -exec grep управляйте парой что группы path/filename.ext:ln#:line содержание на одной строке. Я хочу разделить строку на две последовательных строки во втором файле. Последовательные строки:

path/filename/ext:ln#
contents of the line itself

Я мог записать программу, чтобы сделать это, но я задался вопросом это, там был alteadu команда, которая сделает это?

3
задан 3 September 2015 в 05:25

2 ответа

sed с готовностью делает это:

$ echo 'path/filename.ext:ln#:line contents' | sed -r 's/([^:]*:[^:]*):/\1\n/'
path/filename.ext:ln#
line contents

regex ([^:]*:[^:]*): ищет первые два разделенных от двоеточия поля и сохраняет их в группе 1. Текст замены, \1\n, помещает новую строку после тех двух полей.

Улучшение

, Если само имя файла содержит двоеточие, это, конечно, даст неправильные результаты. Как steeldriver предполагает, это может избегаться использования -Z опция к grep, который поместит символ NUL, \x00, вместо двоеточия после имени файла. Например:

grep -ZHn 'regex' * | sed -r 's/\x00([^:]*):/:\1\n/'

Или, если возможности find требуются:

find . -type f -exec grep -ZHn 'regex' {} + | sed -r 's/\x00([^:]*):/:\1\n/'

Это будет работать, даже если двоеточия появятся в имени файла или строке, подобранной, или оба.

2
ответ дан 1 December 2019 в 15:37

Ваш вопрос и мое понимание его

Ваш вопрос в настоящее время испытывает недостаток в конкретных примерах входа и желаемого вывода, следовательно я попытаюсь ответить на Ваш ответ насколько я понимаю и редактирование соответственно, когда Вы обеспечите больше информации.

Путем я понимаю, что Ваш вопрос прямо сейчас состоит в том, что Вы выполняете что-то вдоль следующих строк:

find /path/to/directory -exec grep -H -n 'SomeString' {} \;

Который приводит к результату, который является чем-то вроде этого:

$ find /home/$USER/fortesting -type f -exec grep -H -n 'HelloWorld' {} \;              
/home/serg/fortesting/file3:1:HelloWorld
/home/serg/fortesting/file1:4:HelloWorld

Или в целом /path/to/file:lineNumber:String

Возможные решения

Соответственно достаточно это - задание для awk: у Вас есть 3 поля, разделенные двоеточием (разделитель полей), который переводит в код awk awk -F":" '{printf $1 FS $2 FS "\n" $3 "\n" }' Таким образом мы можем сделать следующее:

$ find /home/$USER/fortesting -type f -exec grep -H -n 'HelloWorld' {} \; | awk -F ":" '{printf $1 FS $2 FS "\n" $3 "\n" }'       
/home/xieerqi/fortesting/file3:1:
HelloWorld
/home/xieerqi/fortesting/file1:4:
HelloWorld

Теперь, awk универсальный инструмент; мы можем имитировать вывод find -exec grep с 'находят - должностное лицо awk' (awk код здесь)', который будет уже быть обработанным и экономит на передаче по каналу.

Рассмотрите рев кода:

$ find $PWD -type f -exec awk  '/HelloWorld/ {print FILENAME":"FNR"\n"$0 }' {} \;                                                  
/home/xieerqi/fortesting/file3:1
HelloWorld
/home/xieerqi/fortesting/file1:4
HelloWorld

Меньше передачи по каналу и содержание обрабатываются, поскольку они найдены. Кроме того, если файл будет иметь двоеточие на свое имя, то этот код все еще обработает его правильно, так как мы не в зависимости от разделителей полей, а скорее печати переменного ИМЕНИ ФАЙЛА, сопровождаемого двоеточием, сопровождаемым FNR (входное рекордное число в текущем входном файле), и найденная строка, разделенная новой строкой.

Эффективность

Теперь, позволяет, полагают, что эффективность как количество файлов идет большая. Во-первых, я создаю файлы file1 кому: file1000, и затем мы используем /usr/bin/time протестировать каждую версию команды.

$ echo 'HelloWorld' | tee file{$(seq -s',' 1 1000)}
$ /usr/bin/time find /home/$USER/fortesting -type f -exec grep -H -n 'HelloWorld' {} \; | awk -F ":" '{printf $1 FS $2 FS "\n" $3 "\n" }'  > /dev/null
0.04user 0.34system 0:03.09elapsed 12%CPU (0avgtext+0avgdata 2420maxresident)k
0inputs+0outputs (0major+113358minor)pagefaults 0swaps

$ /usr/bin/time find $PWD -type f -exec awk  '/HelloWorld/ {print FILENAME":"FNR"\n"$0 }' {} \; > /dev/null                        
0.82user 2.03system 0:04.25elapsed 67%CPU (0avgtext+0avgdata 2856maxresident)k
0inputs+0outputs (0major+145292minor)pagefaults 0swaps

Таким образом, долгая версия, кажется, более эффективна, занимает меньше времени и процента использования ЦП.

Теперь, вот компромисс - изменение \; кому: + :

/usr/bin/time find $PWD -type f -exec awk '/HelloWorld/ {print FILENAME":"NR"\n"$0 }' {} +

Что делает + оператор делает? Большая разница - это + говорит должностному лицу перечислять столько же файлов, сколько введено к awk управляйте как возможные, в то время как \; делает awk позвоните каждый раз для каждого единственного найденного файла.

$ /usr/bin/time find $PWD -type f -exec awk  '/HelloWorld/ {print FILENAME":"FNR"\n"$0 }' {} + > /dev/null                         
0.00user 0.02system 0:00.02elapsed 74%CPU (0avgtext+0avgdata 3036maxresident)k
0inputs+0outputs (0major+398minor)pagefaults 0swaps

Эй, намного быстрее, правильно? Хотя все еще тяжелый на ЦП.

Вывод в другой файл

Что касается вывода в другой файл, добавьте использование > оператор для перенаправления

3
ответ дан 1 December 2019 в 15:37

Другие вопросы по тегам:

Похожие вопросы: