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 POSIXbackup_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 ofsh -c
, becauseprintf %q
only guarantees compatibility with the same shell (so ksh’sprintf %q
generates code that ksh is guaranteed to parse, etc) in corner cases. (It’s possible to write a helper that uses the Pythonshlex
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 oflocal
, not that ofsomecommand
; being in the habit of separatinglocal var
from actual assignment tovar
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}"