Write output from dynamic queries in PL/pgSQL to CSV file
There is a massively simpler solution.
SQL code
After removing much of the cruft, it boils down to this:
DO
$do$
DECLARE
   customer_schema text;
BEGIN
   FOR customer_schema IN
      SELECT cname FROM customer
   LOOP 
      EXECUTE format('COPY (
         SELECT p.*, t1.*, t2.* -- etc. Or just: *
         FROM        %1$I.portal         p
         LEFT   JOIN %1$I.p_fb_config    t1 USING (pid)
         LEFT   JOIN %1$I.p_gplus_config t2 USING (pid)
         -- etc.
         ) TO $$/absolute/path/to/my/%1$I_file.csv$$;'
       , customer_schema);
   END LOOP;
 END 
$do$


Simplify the format() call. Reuse the same parameter many times. Read the documentation here.

No need to stuff the cnames from your customer table into an array for processing. Just use a FOR loop.

Standard techniques like table aliases and the USING clause help to simplify SQL code.


Shell function
Adding that back into your shell function:
PG_CMD_TP=&quot;psql -d portal -U portal -t -P format=unaligned -c &quot;
abc()
{
 $PG_CMD_TP '
DO
$do$
DECLARE
   customer_schema text;
BEGIN
   FOR customer_schema IN
      SELECT cname FROM customer
   LOOP 
      EXECUTE format($f$COPY (
         SELECT *
         FROM        %1$I.portal         p
         LEFT   JOIN %1$I.p_fb_config    t1 USING (pid)
         LEFT   JOIN %1$I.p_gplus_config t2 USING (pid)
         ) TO $p$/absolute/path/to/my/%1$s_file.csv$p$;$f$
       , customer_schema);
   END LOOP;
 END 
$do$';
}

abc

Voilá.

Enclose the whole query in single quotes for the scope of the shell. Unlike with double quotes the shell does not try any substitutions and the string is passed as is (special characters like $ lose their special meaning, too).

Switch to dollar-quoting in the plpgsql code to go along with the outer single quotes in the shell (or escape all single quotes properly).

For a quick solution I named the output files after customer_schema in the example. You may need to sanitize it to be a legal for filename syntax.