Construye un comando dinámicamente

9

Estoy trabajando en un script y necesito construir el tarcomando dinámicamente.

Aquí hay dos ejemplos para ilustrar lo que estoy tratando de hacer:

#!/bin/bash

TAR_ME="/tmp"

EXCLUDE=("/tmp/hello hello" "/tmp/systemd*" "/tmp/Temp*")
_tar="tar "`printf -- '--exclude="%s" ' "${EXCLUDE[@]}"`" -zcf tmp.tar.gz"
echo COMMAND: "${_tar}"
${_tar} "$TAR_ME"

echo -e "\n\nNEXT:\n\n"

EXCLUDE=("--exclude=/tmp/hello\ hello" "--exclude=/tmp/systemd*" "--exclude=/tmp/Temp*")
_tar="tar "`printf -- '%s ' "${EXCLUDE[@]}"`" -zcf test.tar.gz"
echo COMMAND: "${_tar}"
${_tar} "$TAR_ME"

Quiero poder usarlo _tarcomo comando, pude hacerlo funcionar con la ruta clásica, pero necesito que funcione con espacios en el nombre de las carpetas. Y cada vez recibí errores que se ven así:

COMMAND: tar --exclude="/tmp/hello hello" --exclude="/tmp/systemd*" --exclude="/tmp/Temp*"  -zcf tmp.tar.gz /tmp
tar: hello": Cannot stat: No such file or directory

COMMAND: tar --exclude=/tmp/hello\ hello --exclude=/tmp/systemd* --exclude=/tmp/Temp*  -zcf test.tar.gz 
tar: hello: Cannot stat: No such file or directory

Solo una cosa que necesita saber, necesito que mi script funcione en máquinas muy antiguas, lo que significa que no puedo usar las últimas funciones de bash.

ShellCode
fuente
Creo que la opción --exclude solo puede aceptar una sola cadena después de ella. Sin embargo, puede tener múltiples declaraciones --excluir. Tal vez intente "--exclude = / tmp / hello --exclude = hello" ¡Vaya! No importa. Entendí mal.
Lewis M
@LewisM Creo que OP quiere excluir el directorio "/ tmp / hello hello" (sí, con un espacio.
Archemar
@ShellCode qué pasa citando también excluyen, por ejemplo, "--exclude = / tmp / hola hola"
Archemar
Si. Es por eso que puse la declaración de Oops más tarde. :)
Lewis M
¿Qué hay de poner evaldelante de la ejecución?
jimmij

Respuestas:

11

No intentes hacer una cadena ejecutable. En su lugar, cree los argumentos en una matriz y úselos cuando llame tar(ya está usando una matriz correctamente para EXCLUDE):

#!/bin/bash

directory=/tmp

exclude=( "hello hello" "systemd*" "Temp*" )

# Now build the list of "--exclude" options from the exclude array:
for elem in "${exclude[@]}"; do
    exclude_opts+=( --exclude="$directory/$elem" )
done

# Run tar
tar -cz -f tmp.tar.gz "${exclude_opts[@]}" "$directory"

Con /bin/sh:

#!/bin/sh

directory=/tmp

set -- "hello hello" "systemd*" "Temp*"

# Now build the list of "--exclude" options from the $@ array
# (overwriting the values in $@ while doing so)
for elem do
    set -- "$@" --exclude="$directory/$elem"
    shift
done

# Run tar
tar -cz -f tmp.tar.gz "$@" "$directory"

Tenga en cuenta la cita de $@en el shcódigo y de ambos ${exclude[@]}y ${exclude_opts[@]}en el bashcódigo. Esto garantiza que las listas se expandan a elementos citados individualmente.

Relacionado:

Kusalananda
fuente
2
mix(){
        p=$1; shift; q=$1; shift; c=
        i=1; for a; do c="$c $q \"\${$i}\""; i=$((i+1)); done
        eval "${p%\%*}$c${p#*\%}"
}
mix 'tar % -zcf tmp.tar.gz' --exclude "/tmp/hello hello" "/tmp/systemd*" "/tmp/Temp*"

EXCLUDE=("/tmp/hello hello" "/tmp/systemd*" "/tmp/Temp*")
mix 'tar % -zcf tmp.tar.gz' --exclude "${EXCLUDE[@]}"

Extendiendo la respuesta aquí . Esto no depende de ningún bashismo, también funcionará bien con Debian /bin/shy con busybox.

Mosvy
fuente
Muchas gracias por su ayuda, pero no me gusta mucho la evaluación, es bastante peligroso ... Además, este código es bastante difícil de entender, ¿no tiene algo más fácil? : / El script se distribuirá, así que tengo que mantenerlo lo más simple posible ...
ShellCode
No es peligroso Ejecútalo con set -x. ¿Qué es exactamente lo que no entiendes?
mosvy
Además, lea la respuesta original en stackoverflow. Incluye una demo.
mosvy
Sin embargo, funciona bastante bien ... Esperando a ver si alguien tiene una respuesta más clara, de lo contrario, aceptaré la suya. Tal vez no hay nada de malo con ese código, pero cada vez que veo una evaluación, me temo que el código podría conducir a la inyección de comandos, es por eso que trato de evitarlo
ShellCode
He actualizado la respuesta con una solución para los índices> 9. Puede reemplazar el eval con un eco para ver lo que realmente está recibiendo (la eval no ve los nombres de archivo)
mosvy