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 %qto 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 -cinstead ofsh -c, becauseprintf %qonly guarantees compatibility with the same shell (so ksh’sprintf %qgenerates code that ksh is guaranteed to parse, etc) in corner cases. (It’s possible to write a helper that uses the Pythonshlexmodule 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 varfrom actual assignment tovaravoids 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}"