Créer une commande dynamiquement

9

Je travaille sur un script et j'ai besoin de construire la tarcommande dynamiquement.

Voici deux exemples pour illustrer ce que j'essaie de faire:

#!/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"

Je veux pouvoir utiliser _tarune commande, j'ai pu le faire fonctionner avec un chemin classique, mais j'en ai besoin pour travailler avec des espaces dans le nom des dossiers. Et chaque fois que j'ai des erreurs qui ressemblent à:

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

Juste une chose que vous devez savoir, j'ai besoin que mon script fonctionne sur de très vieilles machines, ce qui signifie que je ne peux pas utiliser les dernières fonctionnalités de bash.

ShellCode
la source
Je pense que l'option --exclude ne peut accepter qu'une seule chaîne après. Vous pouvez cependant avoir plusieurs instructions --exclude. Essayez peut-être "--exclude = / tmp / hello --exclude = hello" Oups. Ça ne fait rien. J'ai mal compris.
Lewis M
@LewisM Je pense que OP veut exclure le répertoire "/ tmp / hello hello" (oui, avec un espace.
Archemar
@ShellCode que diriez-vous de citer tous les exclus, par exemple "--exclude = / tmp / hello hello"
Archemar
Ouais. C'est pourquoi j'ai mis la déclaration Oops plus tard. :)
Lewis M
Que diriez-vous de mettre evaldevant l'exécution?
jimmij

Réponses:

11

N'essayez pas de créer une chaîne exécutable. Au lieu de cela, créez les arguments dans un tableau et utilisez-le lors de l'appel tar(vous utilisez déjà un tableau correctement pour 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"

Avec /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"

Notez la citation de $@dans le shcode et des deux ${exclude[@]}et ${exclude_opts[@]}dans le bashcode. Cela garantit que les listes sont étendues aux éléments entre guillemets individuels.

En relation:

Kusalananda
la source
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[@]}"

Étendre la réponse ici . Cela ne repose sur aucun bashisme, cela fonctionnera également très bien avec Debian /bin/shet avec busybox.

mosvy
la source
Merci beaucoup pour votre aide, mais je n'aime pas vraiment l'eval, c'est assez dangereux ... De plus, ce code est assez difficile à comprendre, vous n'avez pas quelque chose de plus simple? : / Le script sera distribué donc je dois le garder aussi simple que possible ...
ShellCode
Ce n'est pas dangereux. Exécutez-le avec set -x. Qu'est-ce que tu ne comprends pas exactement?
2018
Lisez également la réponse d'origine sur stackoverflow. Il comprend une démo.
2018
Cela fonctionne très bien cependant ... En attendant de voir si quelqu'un a une réponse plus claire, sinon j'accepterai la vôtre. Peut-être qu'il n'y a rien de mal à ce code, mais chaque fois que je vois un eval, j'ai peur que le code puisse conduire à une injection de commande, c'est pourquoi j'essaye de l'éviter
ShellCode
J'ai mis à jour la réponse avec un correctif pour les index> 9. Vous pouvez remplacer l'eval par un écho pour voir ce qui se passe réellement (l'eval ne voit pas les noms de fichiers)
mosvy