Как удалить отдельные слова из строк текстового файла?
Мой текстовый файл выглядит так:
Liquid penetration 95% mass (m) = 0.000205348
Liquid penetration 95% mass (m) = 0.000265725
Liquid penetration 95% mass (m) = 0.000322823
Liquid penetration 95% mass (m) = 0.000376445
Liquid penetration 95% mass (m) = 0.000425341
теперь я хочу удалить Liquid penetration 95% mass (m)
из моих строк, чтобы получить только значения. Как я должен это делать?
8 ответов
Если есть только один =
подписать, вы можете удалить все до и в том числе =
как это:
$ sed -r 's/.* = (.*)/\1/' file
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341
Если вы хотите изменить исходный файл, используйте -i
Вариант после тестирования:
sed -ri 's/.* = (.*)/\1/' file
Заметки
-r
использовать ERE, чтобы нам не пришлось бежать(
а также)
s/old/new
замещатьold
сnew
.*
любое количество любых символов(things)
спастиthings
позже ссылаться на\1
,\2
, так далее.
Это работа для awk
; при условии, что значения встречаются только в последнем поле (согласно вашему примеру):
awk '{print $NF}' file.txt
NF
являетсяawk
переменная, расширяется до количества полей в записи (строке), следовательно$NF
(Обратите внимание$
впереди) содержит значение последнего поля.
Пример:
% cat temp.txt
Liquid penetration 95% mass (m) = 0.000205348
Liquid penetration 95% mass (m) = 0.000265725
Liquid penetration 95% mass (m) = 0.000322823
Liquid penetration 95% mass (m) = 0.000376445
Liquid penetration 95% mass (m) = 0.000425341
% awk '{print $NF}' temp.txt
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341
Я решил сравнить разные решения, перечисленные здесь. Для этой цели я создал большой файл на основе содержимого, предоставленного OP:
Я создал простой файл с именем
input.file
:$ cat input.file Liquid penetration 95% mass (m) = 0.000205348 Liquid penetration 95% mass (m) = 0.000265725 Liquid penetration 95% mass (m) = 0.000322823 Liquid penetration 95% mass (m) = 0.000376445 Liquid penetration 95% mass (m) = 0.000425341
Затем я выполнил этот цикл:
for i in {1..100}; do cat input.file | tee -a input.file; done
Окно терминала было заблокировано. Я выполнил
killall tee
из другого терминала. Затем я проверил содержимое файла с помощью команд:less input.file
а такжеcat input.file
, Выглядело хорошо, кроме последней строчки. Поэтому я удалил последнюю строку и создал резервную копию:cp input.file{,.copy}
(из-за команд, которые используют опцию inplace).Окончательный счетчик строк в файле
input.file
2 192 473. Я получил этот номер по командеwc
:$ cat input.file | wc -l 2192473
Вот результат сравнения:
grep -o '[^[:space:]]\+$'
$ time grep -o '[^ [: space:]] \ + $' input.file> output.file реальный 0m58.539s пользователь 0m58.416s sys 0m0.108s
sed -ri 's/.* = (.*)/\1/'
$ time sed -ri 's /.* = (. *) / \ 1 /' input.file реальный 0m26.936s пользователь 0m22.836s sys 0m4.092s
В качестве альтернативы, если мы перенаправим вывод в новый файл, команда будет быстрее:
$ time sed -r 's /.* = (. *) / \ 1 /' input.file> output.file реальный 0m19.734s пользователь 0m19.672s sys 0m0.056s
gawk '{gsub(".*= ", "");print}'
$ time gawk '{gsub (". * =", ""); print}' input.file> output.file реальный 0m5.644s пользователь 0m5.568s sys 0m0.072s
rev | cut -d' ' -f1 | rev
$ time rev input.file | cut -d '' -f1 | rev> output.file реальный 0m3.703s пользователь 0m2.108s sys 0m4.916s
grep -oP '.*= \K.*'
$ time grep -oP '. * = \ K. *' input.file> output.file реальный 0m3.328s пользователь 0m3.252s sys 0m0.072s
sed 's/.*= //'
(соответственно-i
опция делает команду в несколько раз медленнее)$ time sed 's /.*= //' input.file> output.file реальный 0m3.310s пользователь 0m3.212s sys 0m0.092s
perl -pe 's/.*= //'
(-i
опция не дает большой разницы в производительности)$ time perl -i.bak -pe 's /.*= //' input.file реальный 0m3.187s пользователь 0m3.128s sys 0m0.056s
$ time perl -pe 's /.*= //' input.file> output.file реальный 0m3.138s пользователь 0m3.036s sys 0m0.100s
awk '{print $NF}'
$ time awk '{print $ NF}' input.file> output.file реальный 0m1.251s пользователь 0m1.164s sys 0m0.084s
cut -c 35-
$ time cut -c 35- input.file> output.file реальный 0m0.352s пользователь 0m0.284s sys 0m0.064s
cut -d= -f2
$ time cut -d = -f2 input.file> output.file real 0m0.328s пользователь 0m0.260s sys 0m0.064s
С grep
и -P
за то PCRE
(Интерпретировать паттерн как совместимую регулярную экспрессию) и -o
печатать сопоставленный образец в одиночку. \K
notify будет игнорировать подходящую часть перед собой.
$ grep -oP '.*= \K.*' infile
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341
Или вы могли бы использовать cut
команда вместо
cut -d= -f2 infile
Поскольку префикс строки всегда имеет одинаковую длину (34 символа), вы можете использовать cut
:
cut -c 35- < input.txt > output.txt
Обратное содержимое файла с rev
направить вывод в cut
с пробелом в качестве разделителя и 1 в качестве целевого поля, затем снова измените его, чтобы получить исходное число:
$ rev your_file | cut -d' ' -f1 | rev
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341
Это просто, кратко и легко написать, понять и проверить, и мне лично это нравится:
grep -oE '\S+$' file
grep
в Ubuntu, когда вызывается с -E
или же -P
берет стенографию \s
обозначать символ пробела (на практике обычно это пробел или табуляция) и \S
иметь в виду все, что не одно. Использование квантификатора +
и конец строки якоря $
, образец \S+$
соответствует одному или нескольким непробелам в конце строки. Ты можешь использовать -P
вместо -E
; значение в этом случае то же самое, но используется другой механизм регулярных выражений, поэтому они могут иметь разные характеристики производительности.
Это эквивалентно прокомментированному решению Avinash Raj (только с более простым и компактным синтаксисом):
grep -o '[^[:space:]]\+$' file
Эти подходы не сработают, если после числа могут стоять пробелы. Они могут быть изменены, чтобы они сделали, но я не вижу смысла вдаваться в это здесь. Хотя иногда поучительно обобщать решение для работы в большем количестве случаев, нецелесообразно делать это почти так часто, как полагают люди, потому что обычно нет способа узнать, каким из множества несовместимых способов может в конечном итоге понадобиться проблема быть обобщенным.
Производительность иногда является важным фактором. Этот вопрос не предусматривает, что входные данные очень велики, и вполне вероятно, что каждый опубликованный здесь метод достаточно быстр. Однако, если скорость требуется, вот небольшой тест для входного файла из десяти миллионов строк:
$ perl -e 'print((<>) x 2000000)' file > bigfile
$ du -sh bigfile
439M bigfile
$ wc -l bigfile
10000000 bigfile
$ TIMEFORMAT=%R
$ time grep -o '[^[:space:]]\+$' bigfile > bigfile.out
819.565
$ time grep -oE '\S+$' bigfile > bigfile.out
816.910
$ time grep -oP '\S+$' bigfile > bigfile.out
67.465
$ time cut -d= -f2 bigfile > bigfile.out
3.902
$ time grep -o '[^[:space:]]\+$' bigfile > bigfile.out
815.183
$ time grep -oE '\S+$' bigfile > bigfile.out
824.546
$ time grep -oP '\S+$' bigfile > bigfile.out
68.692
$ time cut -d= -f2 bigfile > bigfile.out
4.135
Я запускал его дважды в случае, если порядок имел значение (как это иногда бывает для задач, связанных с вводом / выводом), и потому, что у меня не было машины, которая не выполняла другие операции в фоновом режиме, которые могли бы исказить результаты. Из этих результатов я делаю следующие выводы, по крайней мере, предварительно и для входных файлов того размера, который я использовал:
Вот Это Да! Переходя
-P
(использовать PCRE), а не-G
(по умолчанию, когда диалект не указан) или-E
сделалgrep
быстрее более чем на порядок. Поэтому для больших файлов лучше использовать эту команду, чем приведенную выше:grep -oP '\S+$' file
ВОТ ЭТО ДА!!
cut
метод в ответе αғs нιη,cut -d= -f2 file
, на порядок быстрее, чем даже более быстрая версия моего пути! Он также стал победителем в тесте PA4080, который охватывал больше методов, чем этот, но с меньшим вводом - и именно поэтому я выбрал его, из всех других методов, для включения в мой тест. Если производительность важна или файлы огромны, я думаю, чтоcut
метод должен быть использован.Это также служит напоминанием о том, что простой
cut
а такжеpaste
Утилиты не должны быть забыты, и, возможно, их следует отдавать предпочтение, когда это применимо, хотя существуют более сложные инструменты, такие какgrep
которые часто предлагаются в качестве решений первой линии (и которые я лично больше использую).
perl
- замени шаблон /.*= /
с пустой строкой //
:
perl -pe 's/.*= //' input.file > output.file
perl -i.bak -pe 's/.*= //' input.file
От
perl --help
:-e program one line of program (several -e's allowed, omit programfile) -p assume loop like -n but print line also, like sed -i[extension] edit <> files in place (makes backup if extension supplied)
sed
- заменить шаблон пустой строкой:
sed 's/.*= //' input.file > output.file
или (но медленнее, чем указано выше):
sed -i.bak 's/.*= //' input.file
- Я упоминаю этот подход, потому что он в несколько раз быстрее, чем в ответе Занны.
gawk
- заменить шаблон ".*= "
с пустой строкой ""
:
gawk '{gsub(".*= ", "");print}' input.file > output.file
От
man gawk
:gsub(r, s [, t]) For each substring matching the regular expression r in the string t, substitute the string s, and return the number of substitutions. If t is not supplied, use $0...