I think you need to replace the double quotes with single quotes to prevent your shell from expanding $g. From man bash:
Enclosing characters in double quotes preserves the literal value of all
characters within the quotes, with the exception of $, `, \, and,
when history expansion is enabled, !.
Currently, your shell expands $g inside your string, as if it was an environment variable. But it's probably not defined, thus expands into an empty string. So, even though you've typed:
vim -c "2,$g/^dog/d|wq" poo.txt
Vim doesn't receive the command:
2,$g/^dog/d|wq
... but:
2,/^dog/d|wq
This command deletes all the lines from the one whose address is 2, to the next one which starts with dog (in your case it's the 3rd line). Then, it saves and quit.
But even if you replace the quotes, there's still a problem in your command.
From :h :bar:
These commands see the '|' as their argument, and can therefore not be
followed by another Vim command:
...
:global
...
The bar is interpreted by :g as a part of its argument, not as a command termination. In your case, it means that whenever it finds a line starting with dog, it will delete it, then immediately save and quit. So, if there are several dog lines, only the first one will be deleted, because :g will have saved and quit after processing the 1st one.
You need to hide |wq from :g, either by wrapping the global command inside a string and executing it with :execute, or by moving wq in another -c {cmd}. All in all, you could try:
vim -c 'exe "2,\$g/^dog/d" | wq' poo.txt
or
vim -c '2,$g/^dog/d' -c 'wq' poo.txt
or
vim -c '2,$g/^dog/d' -cx poo.txt