Construa um comando dinamicamente

9

Estou trabalhando em um script e preciso criar o tarcomando dinamicamente.

Aqui estão dois exemplos para ilustrar o que estou tentando fazer:

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

Eu quero poder usar _tarcomo um comando, consegui fazê-lo funcionar com o caminho clássico, mas preciso que ele funcione com espaços no nome das pastas. E toda vez que recebo erros que se parecem com:

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

Apenas uma coisa que você precisa saber: preciso que meu script funcione em máquinas muito antigas, o que significa que não posso usar os últimos recursos do bash.

ShellCode
fonte
Acredito que a opção --exclude só pode aceitar uma única sequência após ela. Você pode ter várias instruções --exclude embora. Talvez tente "--exclude = / tmp / hello --exclude = hello" Ops. Deixa pra lá. Eu entendi errado.
Lewis M
@LewisM Acho OP quer excluir diretório "/ tmp / Olá, olá" (sim, com um espaço.
Archemar
@ShellCode, que tal citar todos os excluídos, por exemplo "--exclude = / tmp / hello hello"
Archemar 4/18
Sim. É por isso que coloco a declaração Oops mais tarde. :)
Lewis M
Que tal colocar evalna frente da execução?
jimmij

Respostas:

11

Não tente criar uma string executável. Em vez disso, crie os argumentos em uma matriz e use-os ao chamar tar(você já está usando uma matriz corretamente 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"

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

Observe a citação de $@no shcódigo e de ambos ${exclude[@]}e ${exclude_opts[@]}no bashcódigo. Isso garante que as listas sejam expandidas para elementos entre aspas individuais.

Relacionado:

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

Estendendo a resposta aqui . Isso não depende de nenhum basismo, mas também funcionará bem com o debian /bin/she com busybox.

mosvy
fonte
Muito obrigado por sua ajuda, mas eu realmente não gosto da avaliação, é bastante perigoso ... Além disso, esse código é bastante difícil de entender, você não tem algo mais fácil? : / O script será distribuído então eu tenho que mantê-lo o mais simples possível ...
shellcode
Não é perigoso. Execute-o com set -x. O que exatamente você não entende?
mosvy
Além disso, leia a resposta original no stackoverflow. Inclui uma demonstração.
mosvy
Mas funciona muito bem ... Esperando para ver se alguém tem uma resposta mais limpa, caso contrário, eu aceito a sua. Talvez não há nada de errado com esse código, mas cada vez que vejo um eval, temo o código poderia levar a injeção de comandos, é por isso que eu tento evitá-lo
shellcode
Eu atualizei a resposta com uma correção para índices> 9. Você pode substituir o eval com um eco para ver o que está realmente recebendo (o eval não vê os nomes de arquivos)
mosvy