Filtering Lines by a Pattern Without a Command Line
Published 8/7/2026 · 11 min read · Text & language tools
Daniel Okonkwo — Front-end developer and tech writer at OneKitly
Web performance · File formats
Checked against 3 sources
filter-lines splits your text on line breaks, tests each line for a substring, and keeps or drops it. The match is String.prototype.includes on a literal term — there is no regular-expression engine behind the box. Typing ^ERROR into a log returned nothing; so did ERROR|WARN; so did [a-z]+. Each produced an empty output panel with no error message and no note that the pattern had been read literally, which is the one behaviour to know before you start. The opposite is also true and more comforting: an invalid regular expression is harmless here, because it is not a regular expression. Typing a single opening parenthesis matched the line that contains one, exactly as you would want. Ignore case is on by default, so filtering for apple kept the line reading APPLE juice as well as apple pie; grep on a terminal is case-sensitive by default and this tool is not, which is a difference worth holding in mind if you are used to one. Action offers Keep matching lines and Remove matching lines, which correspond to grep and grep -v. Leaving the term empty returns your text unchanged rather than nothing. Two details from the runs: remove mode keeps blank lines, because a blank line does not contain your term, so a filtered log comes back with the gaps where the removed lines were; and all output is joined with plain line feeds, so a Windows file loses its carriage returns on the way through.
This is grep for people who do not use grep, with one important difference: the match is a plain substring, so a real regular expression returns an empty box and no error. Every claim here was checked by running the tool.
What grep does, and which half of it is here
grep reads a text a line at a time, tests each line against a pattern, and writes out the lines that matched. The POSIX specification defines it that way and defines the pattern as a regular expression — a small language in which a caret means start of line, a vertical bar means either, square brackets mean any one of these characters and a plus means one or more of the thing before it. The invert option turns the test around and prints the lines that did not match. The whole idea is about forty years old and it is still the fastest way to answer a question about a log file.
filter-lines implements the loop and the invert option and stops there. The pattern language is not implemented, and importantly it is not partially implemented either: the term you type is compared as a run of characters, whole and unchanged. That is a legitimate design for a box on a web page — most people filtering a list want lines containing the word invoice, not a grammar — but it means the two tools take the same input and give different answers, and the tool that is wrong about it never says so.
The empty box, and why it is empty
Three patterns were run against the same seven-line log. ^ERROR returned nothing, although two lines start with ERROR, because no line contains a literal caret followed by the letters E, R, R, O, R. ERROR|WARN returned nothing, although the log has one of each, because no line contains a literal vertical bar between those two words. [a-z]+ returned nothing, for the same reason: no line contains a square bracket. In every case the output panel was simply empty. Nothing was highlighted, nothing was reported, and nothing suggested that the term had been read differently from the way it was meant.
This is the failure mode that matters, and it is the reverse of the one the phrase invalid regex would suggest. A malformed pattern is the safe case. Typing a single opening parenthesis — which would make a regular-expression engine throw an error about an unterminated group — matched the line containing a parenthesis and returned it. So did a lone full stop, which in the regular-expression language means any character and here means a full stop: on a list containing both a.b and aXb it returned only a.b. If you catch yourself typing regular-expression syntax into this box, the reflex to build is to look at the output and ask whether an empty result is plausible, because the tool will not ask it for you.
Case, accents, and the space you did not mean to type
Ignore case is on out of the box, and it lower-cases both the line and the term before comparing. Filtering a five-line fruit list for apple kept apple pie, APPLE juice and pineapple tart; turning the toggle off dropped APPLE juice and kept the other two. The pineapple line is the reminder that this is a substring test and not a word test: apple is inside pineapple, so the line matches, and there is no whole-word option to stop it. If you need word boundaries, add the surrounding spaces to your term and accept that you will miss the word at the start or end of a line.
Two smaller traps came out of the same runs. A leading space in the term is part of the term: filtering the same list for a space followed by apple returned nothing, because no line has a space before that word in the position tested. And accents follow the rule from the duplicate-line article — a term whose accented letter is written as a letter plus a combining mark matched nothing in a list whose accented letter is a single code point, even though the two look identical in the search box. Both come back to the same discipline: what you typed is compared exactly as typed, including the parts you cannot see.
Remove mode leaves holes
Switch Action to Remove matching lines and the tool drops every line that contains your term and keeps everything else — which includes the blank lines, because a blank line does not contain your term. Run it over a log with an empty line between two entries and the empty line is still there afterwards. It is not a bug, it is the honest reading of the instruction, but the result rarely looks like what people expected, and a filtered file with a dozen orphaned gaps in it is annoying to read. The fix is one more step: run remove-blank-lines over the output.
One more behaviour worth relying on: an empty term returns your text unchanged, not an empty result. That sounds obvious, but the alternative reading — no term, so nothing matches, so nothing comes out — would be equally defensible and would silently blank your input every time you cleared the field to type a new search. The tool takes the safer branch, and it was checked: with the term box empty, all five lines of the test list came back.
What to do when you really do need a pattern
Most pattern questions can be rewritten as substring questions. Lines that start with a code become lines that contain that code, which is looser but usually fine on a log where the code only appears at the start anyway. Either of two words becomes two passes — filter for the first, note the result, filter the original for the second — because there is no alternation. A range of characters generally becomes several passes or a different tool altogether. When the answer really needs a grammar, the honest advice is that a browser text box is the wrong instrument, and the right one is a terminal or a text editor with a regular-expression search.
One caveat about the sibling tool. find-and-replace, on this same site, also takes a literal term — it escapes every character that has a special meaning before building its pattern, which was read in its source and confirmed. So if you were hoping to use it to fake a regular-expression filter, you cannot. The two tools are consistent with each other, which is the good news; neither of them is grep, which is the news you needed before you started.
| Term typed | What comes back | Why |
|---|---|---|
| apple, Ignore case on (the default) | apple pie, APPLE juice and pineapple tart | Both sides are lower-cased, and the test is substring, not whole word |
| apple, Ignore case off | apple pie and pineapple tart only | APPLE juice no longer contains the exact characters typed |
| ^ERROR on a log whose first two lines start with ERROR | Empty output, no error message | No line contains a literal caret followed by those five letters |
| ERROR|WARN on the same log | Empty output | There is no alternation; the vertical bar is just a character to look for |
| A single opening parenthesis | The line that contains a parenthesis — it works | An invalid regular expression is the safe case, because none is being compiled |
| A single full stop, on a list holding a.b and aXb | a.b only | The full stop matches a full stop, not any character |
| An empty Contains box | The whole text, unchanged | The tool returns early rather than filtering everything away |
| Any term, in Remove matching lines mode, on a text with blank lines | The blank lines survive | A blank line does not contain the term, so it is not a match to remove |
Frequently asked questions
- Can I use a regular expression in the Contains box?
- No. The term is compared literally, character by character, with no pattern language behind it. This was checked on three ordinary patterns — a start-of-line anchor, an alternation and a character class — and all three returned an empty output panel with no message. The behaviour to internalise is that a wrong answer here looks exactly like a correct answer of nothing matched, so treat an empty result as a question rather than a fact: retype the term without its syntax characters and see whether lines appear.
- How do I keep only the lines that start with something?
- You cannot ask for start of line here, because there is no anchor. In practice the substring version is usually good enough: filter for the code or prefix itself, and accept any line that happens to contain it elsewhere. Whether that is acceptable depends on your data, and you can find out cheaply — filter once, then run the same term through in Remove mode and look at what came out. If the removed pile contains nothing you wanted, the loose version was fine. If you genuinely need the anchor, a text editor with a regular-expression search is the tool for it.
- Is the filter case-sensitive?
- Not by default. Ignore case is switched on when the page loads, which is the opposite of grep's default and the opposite of what a habitual terminal user expects. Turn it off from the same panel if you need the distinction — filtering a fruit list for apple with the toggle on kept a line reading APPLE juice, and turning it off dropped that line while keeping the two lower-case ones. The folding is the plain language-independent one, so it handles accented capitals correctly but does not do anything special for a particular language.
- Why does my filtered output have gaps in it?
- Because you used Remove matching lines and your text had blank lines in it. A blank line does not contain your term, so the tool keeps it, and it stays exactly where it was — now surrounded by the space the removed lines used to occupy. Running remove-blank-lines over the result closes them all in one pass. If you are chaining several filters, do the blank-line cleanup once at the end rather than after each step, because each Remove pass will open new gaps.
- Does the tool tell me how many lines matched?
- No count is shown; you get the matching lines and nothing else. If you need the number, the shortest path is to run the result through a line counter, or to use count-occurrences on the original text with the same term — bearing in mind that it counts occurrences and a single line may contain your term twice, so the two numbers can legitimately differ. If the count is what you actually want and the lines are not, count-occurrences is the better starting point.
Articles you may find interesting
All guides →Related tools
Everything here describes what these tools do today, checked by running their own transforms against the exact inputs printed in each article, not what a standard obliges a text tool to do. Line-level text handling has no single authority: what counts as whitespace, whether two accented lines are the same line, and where a URL ends in running prose are decided differently by every program you will ever paste into. Where a tool gets a case wrong, that is said plainly rather than worked around. Before you run any of this over a list you cannot re-export, run it over a copy and compare the line count at both ends.
Sources
Spotted a mistake in this article?