Зніміть рядок видалення з 0, але не з 0,2?


12

У мене є файл, вміст якого схожий на наступний.

0
0
0.2
0
0
0
0

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

Як я можу видалити всі рядки, що містять лише один 0, і зберегти всі ці рядки, починаючи з 0?


2
Можливий дублікат точної
Жульєн Лопес

1
@JulienLopez Це не справжнє питання. Це питання стосується узгодження слова та відповіді -w, яке не вдається.
Sparhawk

Чому ви змушені використовувати grepдля цього завдання? А що саме ви маєте на увазі під одним нулем ? Це дуже схоже на проблему XY .
Roland Illig

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

Відповіді:


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опцією, оскільки ми не використовуємо шаблони регулярних
виразів

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

2
Можливо, що двигун RegEx використовується так часто і настільки широко, що вони реалізували дуже ефективну його версію, але, що "звичайний пошук", ймовірно, не був оновлений протягом 30 років.
Нельсон

@Sparhawk: grepімовірно, є особливий випадок для регулярних виразів без метахарактерів , оскільки це звичайний випадок використання. Це дивно, що fgrepце буде повільніше, але це не дивно, що накладні витрати на помічення цього особливого випадку при складанні короткого шаблону незначні порівняно з часом сканування великого файлу. (Якщо для взагалі потрібен спеціальний випадок, щоб пройти так швидко, порівняно з візерунком з класом символів або x.*y.)
Пітер Кордес

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

28

З грепом:

grep -v "^0$" file

^означає початок рядка, $означає кінець рядка.


2
Ось що просив користувач: уникайте будь-яких рядків, що містять лише 1 "0".
Олів'є

1
Я б не ставив буквальний знак долара всередині подвійних лапок.
користувач541686

@mehrdad не така вже й велика проблема з регулярними виразками, як це, як правило, або остання чарка, або наступна звичка[a-Z0-9]
Sampo Sarrala - 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"
glenn jackman

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варіантом, дякую!
величнийLSD

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.16над ubuntu
Jakub

1
Використовується регулярний вираз GNU \<і \>як межі слова, але це матиме такий же ефект, як-w
glenn jackman

0

Ще одна відповідь заради різноманітності, якщо у вас є функція PCRE grep

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

це виконує негативний підхід, щоб відповідати лініям, які починаються з 0і не супроводжуються крапкою. Потім -vвідкидає невідповідні лінії. Дії ви можете побачити тут


1
Це також видалить такі лінії, як 0123, наприклад , це не те, чого хоче ОП
iruvar

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.