A .htaccess rule runs after the URL has been mapped to a file, so its result takes effect only through an internal redirect: a fresh pass over the new URL, with every rule tried again.

A pass in which no rule fires ends the cycle, as does a substitution equal to its input (initial URL equal rewritten URL ... [IGNORING REWRITE]); otherwise it runs to L vs END's 500. Stoppers, best first: [END]; a condition the output fails (!-f); RewriteCond %{ENV:REDIRECT_STATUS} ^$, true only on pass one, since internal redirects set REDIRECT_STATUS; and an early RewriteRule ^show\.php$ - [L] exempting the target.
The subtle case is a pass-2 redirect. Hiding the front controller with RewriteRule ^show\.php$ / [R=301,L] alone sends /products/7 to /: pass 1 rewrote it to show.php, and pass 2 redirected that. THE_REQUEST, the client's own request line, tells the two apart:
RewriteEngine On
RewriteCond %{THE_REQUEST} ^\S+\s/show\.php[?\s]
RewriteRule ^show\.php$ / [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ show.php [L]$ t /show.php '/show.php?x=1' /products/7 /show.php 301 http://example.com/ /show.php?x=1 301 http://example.com/?x=1 /products/7 200 []
REDIRECT_STATUS gives the same results. A redirect and a rewrite that feed each other loop in the browser instead, until ERR_TOO_MANY_REDIRECTS (Rewrite Loops).