Search code examples
postgresqlbashdockerpg-dump

docker exec pg_dump in bash function


I'm trying to run pg_dump in a docker container, in a bash function, but I'm getting an error can't create : nonexistent directory. The dir does exist, so I suspect that the output redirection (>) is trying to run on the host system when running inside the function. When I run the command outside of the bash function it works.

Note that the psql container has the directory /var/lib/postgresql/backups/ mapped to /usr/share/postgresql/backups/ on the host.

The command that works is,

docker exec -it psql sh -c 'pg_dump -U $PG_USER MY_TABLE > /var/lib/postgresql/backups/my_table.sql

The function that is not working

# @param $1 -- the name of the table to export
# @param $2 -- the filename to export the table to
function backup_table() {
  local table_name="$1"
  local output_path="/var/lib/postgresql/backups/$2"
  docker exec -it psql sh -c 'pg_dump -U "$PG_USER" $table_name > $output_path'
}
backup_table MY_TABLE my_table.sql

Solution

  • The redirection is properly happening inside the container -- but output_path isn't being passed there.


    Assuming PG_USER is defined inside the container:

    backup_table() {                    # POSIX function syntax
      local table_name output_path cmd  # declare all our locals at once
      table_name="$1"
      output_path="/var/lib/postgresql/backups/$2"
    
      # Bash extension, compatible through older releases: substitute
      # escaped values in place of %q placeholders into $cmd
      printf -v cmd 'pg_dump -U "$PG_USER" %q >%q' "$table_name" "$output_path"
      docker exec -it psql bash -c "$cmd"
    }
    backup_table MY_TABLE my_table.sql
    

    Note:

    • We're using POSIX-compliant function declaration syntax, not the widely-incompatible bastardization of ksh function backup_table { and POSIX backup_table() { previously in use.
    • We're using printf %q to escape variables before substituting them into shell syntax, to ensure that the remote shell parses the content back to their original values.
    • We're using bash -c instead of sh -c, because printf %q only guarantees compatibility with the same shell (so ksh's printf %q generates code that ksh is guaranteed to parse, etc) in corner cases. (It's possible to write a helper that uses the Python shlex module if you need to generate escapings compatible with all POSIX shells, but arguably out-of-scope here).
    • Declaring locals ahead-of-time is good form, because local var=$(somecommand) returns the exit status of local, not that of somecommand; being in the habit of separating local var from actual assignment to var avoids the pitfall.

    If you only need to support bash 5.0 or newer, you could replace the printf command with:

    cmd='pg_dump -U "$PG_USER" '"${table_name@Q}"' >'"${output_path@Q}"