grep1-5 Exercise 2: Quoted Strings: Greedy, Lazy, Negated Set, and a Real Quote Inside ======================================================================================= cat > quotes.txt <<'EOF' name="Bob" city="Paris" say "hello" and "bye" EOF printf '%s\n' 'he said "go \"now\"" twice' >> quotes.txt echo "greedy: grep -o '\".*\"'" grep -o '".*"' quotes.txt echo echo "lazy: grep -oP '\".*?\"'" grep -oP '".*?"' quotes.txt echo echo "negated: grep -o '\"[^\"]*\"'" grep -o '"[^"]*"' quotes.txt echo echo "escape-aware (Perl): grep -oP '\"(?:[^\"\\\\]|\\\\.)*\"'" grep -oP '"(?:[^"\\]|\\.)*"' quotes.txt Output: greedy: grep -o '".*"' "Bob" city="Paris" "hello" and "bye" "go \"now\"" lazy: grep -oP '".*?"' "Bob" "Paris" "hello" "bye" "go \" "" negated: grep -o '"[^"]*"' "Bob" "Paris" "hello" "bye" "go \" "" escape-aware (Perl): grep -oP '"(?:[^"\\]|\\.)*"' "Bob" "Paris" "hello" "bye" "go \"now\"" WHY THIS WORKS AS AN ANSWER --------------------------- Greedy takes everything from the first quote to the LAST one on the line, so lines 1 and 2 come out as single long strings (wrong). The lazy pattern and the negated set both give one string at a time and agree on the first two lines. The third line has an escaped quote (\") inside the string. Lazy and the negated set both stop at that escaped quote, so they cut the string short (and then pick up junk). The last pattern says what a string character is: either anything except a quote or backslash, or a backslash followed by any character (so \" is consumed as a pair). That is the case where Perl mode earns its place. Whether the last output is what YOU want depends on whether an escaped quote belongs to the string; the point is that you must decide, and the pattern must say so.