Grep удалить строку с 0, но не с 0,2?


12

У меня есть файл, содержание которого похоже на следующий.

0
0
0.2
0
0
0
0

Мне нужно удалить все строки с одним нулем.
Я думал использовать grep -v "0", но это удаляет также строку, содержащую 0,2. Я видел, что могу использовать эту -wопцию, но это тоже не работает.

Как я могу удалить все строки, содержащие только один 0, и сохранить все эти строки, начиная с 0?



1
@JulienLopez Это не обман этого вопроса. Этот вопрос касается совпадения слова и ответа на него -w, что здесь не так.
Sparhawk

Почему вы вынуждены использовать grepдля этой задачи? А что именно вы подразумеваете под одним нулем ? Это очень похоже на проблему XY .
Роланд Иллиг

1
@RolandIllig это был 1 час перед сном, и я хотел начать обрабатывать серию из 500 000 строк, чтобы проверить, являются ли они биткойн-закрытыми ключами и, если да, получить баланс. В следующий раз, когда у меня было время взглянуть на него, я обработал многие тысячи строк и просто хотел проанализировать любые ненулевые значения.
Филипп Киркбрайд

Ответы:


35
grep -vx 0

От man grep:

-x, --line-regexp
       Select only those matches that exactly match the whole line.
       For a regular expression pattern, this is like parenthesizing
       the pattern and then surrounding it with ^ and $.

-wтерпит неудачу, потому что первый 0в 0.02считается "словом", и, следовательно, эта строка соответствует. Это потому, что за ним следует символ «не слово». Вы можете увидеть это , если вы запустите оригинальную команду без -v, то есть grep -w "0".


Вы также можете использовать эту -Fопцию, так как мы не используем шаблоны регулярных выражений, просто сопоставление строк
glenn jackman

@glennjackman Может быть, я читал это раньше, но я не могу найти это сейчас. Бег с -F(на удивление для меня), кажется, занимает такое же количество времени или даже немного медленнее (~ 5–10%). Следовательно, я не уверен, какое преимущество будет.
Sparhawk

2
Вполне возможно, что механизм RegEx используется так часто и так широко, что они внедрили очень эффективную его версию, но «простой поиск», вероятно, не обновлялся в течение 30 лет.
Нельсон,

@Sparhawk: grepпредположительно, есть специальный случай для регулярных выражений без метасимволов, потому что это распространенный вариант использования. Удивительно, что fgrepэто будет медленнее, но не удивительно, что издержки, связанные с замечанием этого особого случая при составлении короткого шаблона, незначительны по сравнению со временем сканирования большого файла. (Если для такой скорости требуется особый случай, а не шаблон с классом персонажей или x.*y.)
Питер Кордес

Но это может быть упрощением, потому что на самом деле вводом является множество коротких строк (а не одна гигантская строка). Я забыл, если grepраспознает какой-либо символ, кроме \nновой строки, в качестве разделителя строк. Если нет, то неявный ^и $ все еще может превратиться в поиск с фиксированной строкой, как strstr(big_buf, "\n0\n"). (Или 0\nв начале буфера.) Но мы не просто ищем первое совпадение, потенциально находящееся далеко в большом буфере, мы хотим эффективно фильтровать. Но в любом случае, теоретически да, это просто 2-байтовый memcmp в начале каждой строки, и вы надеетесь, что и fgrep, и grep это увидят.
Питер Кордес

28

С grep:

grep -v "^0$" file

^означает начало строки, $означает конец строки.


2
Вот что просил пользователь: избегайте любых строк, содержащих только 1 «0».
Оливье Дюлак

1
Я бы не стал ставить буквальный знак доллара в двойные кавычки.
user541686

@ mehrdad не такая большая проблема с регулярным выражением, поскольку обычно это либо последний символ, либо следующий, который не будет[a-Z0-9]
Сампо Саррала - codidact.org

14

Хотя это grep может быть использовано для этого (как ясно показывают другие ответы), давайте сделаем шаг назад и подумаем о том, что вы на самом деле хотите:

  • У вас есть файл, содержащий цифры
  • Вы хотите выполнить фильтрацию на основе числового значения .

Regex интерпретирует данные последовательности символов. Они не знают о числах, только об отдельных цифрах (и их регулярных комбинациях). Хотя в вашем конкретном случае есть простое решение этого ограничения, в конечном итоге это несоответствие требований.

Если нет очень веской причины для использования grepздесь (например, потому что вы измерили его, и он намного более эффективен, а эффективность в вашем случае имеет решающее значение), я рекомендую использовать другой инструмент.

awkНапример, может фильтровать на основе числовых сравнений, например:

awk '$1 == 0' your_file

Но также, чтобы получить все строки, содержащие числа больше нуля:

awk '$1 > 0' your_file

Я люблю регулярные выражения, это отличный инструмент. Но это не единственный инструмент. Как говорится, если все, что у вас есть grep, все выглядит как обычный язык.


3
Я искренне согласен с тем, что awk здесь может быть более элегантным ... однако, он также будет соответствовать чуть больше, чем ожидает пользователь (каждое числовое значение оценивается в 0). То есть, printf '0\n1\n-1\na\nb\n0\n0 also\n0.0\n-0.0\n0*0\n' | awk '($1 == 0)'будет соответствовать: 0, 0.0а -0.0... а также 0 also! Не просто "0". (что иногда то, что нужно, иногда нет). Если пользователь хочет только «0»: awk '/^0$/' (или grep '^0$'). Также вам следует отредактировать: пользователю нужно добавить, !чтобы отменить тест, чтобы он скрывал 0(и другие нули) и отображал остальное. awk '!( $0 == 0)'
Оливье Дюлак

1
@Olivier, или проверьте строковое значение:$1 == "0"
Гленн Джекман

1
@OlivierDulac Я явно использовал, >а не !=(или, что то же самое ! (… == …)), чтобы подчеркнуть, что это произвольное числовое сравнение, а не просто равенство. Что касается вашего другого комментария, это полностью верно, но тогда мы по существу вернулись на территорию сравнения строк и существующее решение, использующее grepработы (хотя, awkконечно, также работает).
Конрад Рудольф

@KonradRudolph справедливые очки :)
Оливье Дюлак

1
@glennjackman: действительно хороший трюк. Но тогда ОП предпочел бы проверить$0=="0"
Оливье Дюлак

5

grep«s -wнемного запутанным таким образом , что она распадается на исходную строку в слова и не слова составляющих (ничего , кроме букв, цифр или подчеркивания). Поскольку он уже столкнулся с действительным составным словом, 0в 0.02нем утверждена логика отрицания для удаления строки.

Использование sedв этом контексте немного просто, чтобы просто удалить все слова, которые соответствуют

sed '/^0$/d' file

3

Когда строки , которые вы хотите удалить только содержать 0 затем в следующей строке вы можете выбрать те строки, выполнив следующую команду:

grep -v "^0$"

Это напечатает только вхождения, 0которые находятся в конце строки и в начале строки одновременно. -vОпция инвертирует наш выбор.


1
Этот ответ почти идентичен ответу Аркадиуша Драбчика, но вы забыли -v, поэтому он не работает.
Sparhawk

Ты прав. Я печатал, пока он публиковал свой ответ, так что я не видел, что он уже был дан. Я неправильно прочитал эту часть с -vопцией, спасибо!
majesticLSD

0
  • \ b - граница слова

grep -v "\b0\b"

  • соответствует началу строки, вашему шаблону и концу строки

grep -v "^0$"

  • или как @Sparhawk предложил -vx lineregexp

-w работает, но в вашем случае 0.2 два слова, потому что символ точки является разделителем слов.


grep -v "\b0\b"на самом деле не работает здесь. Какую версию grep вы используете?
Аркадиуш Драбчик

работает с grep (BSD grep) 2.5.1-FreeBSDMacOS и grep (GNU grep) 2.16Ubuntu
Jakub Jindra

1
Использование регулярных выражений GNU \<и в \>качестве границ слов, но это будет иметь тот же эффект, что и-w
Гленн Джекман

0

Еще один ответ ради разнообразия, если у вас есть PCRE-включен grep

grep -Pv "^0(?!\.)"

это выполняет отрицательный прогноз, чтобы соответствовать линиям, которые начинаются с 0и не сопровождаются точкой. Затем -vотбрасывает несовпадающие строки. Вы можете увидеть в действии здесь


1
Это также удалит такие строки, как 0123, что не то, что хочет ОП
iruvar

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.