Bash: Fornece um array construído para funcionar como uma lista de argumentos?

0

Eu tenho esse problema com o borgbackup, mas como a reação é a mesma, usarei rsync no meu exemplo.

Eu quero construir uma matriz de argumentos adicionando um prefixo a cada um e, em seguida, dar essa matriz para rsync . Mas rsync age como se não existisse.

Com este script:

#!/usr/bin/env bash
#

declare -a exclude_String
for excludestr in $(cat ./list); do
    exclude_String+=(--exclude=$excludestr)
done
rsync "${exclude_String[@]}" . $Destination

e ./list :

'/home/*'
'*.vim*'

Um ps do processo em execução mostra o argumento corretamente:

/usr/bin/rsync --exclude='*.vim*' --exclude='/home/*' . DESTINATION

Mas rsync ainda age como se não estivesse lá. Eu encontrei esta questão sobre o assunto, então eu tentou:

#!/usr/bin/env bash
#

declare -a exclude_String
exclude_String+=(--exclude='/home/*')
exclude_String+=(--exclude='*.vim*')

rsync "${exclude_String[@]}" . $Destination

E isso realmente funcionou.

Eu ainda não entendi muito sobre expansão de shell e coisas assim, eu tentei citar e aspas duplas em todo o lugar apenas para ver se eu poderia encontrar algo, mas sem sorte.

Alguma ideia de como conseguir isso?

    
por Vlycop Doo 31.07.2018 / 23:40

2 respostas

2

Se o arquivo realmente contiver '/home/*' , com as aspas, as aspas estarão lá no valor de excludestr no loop e, em seguida, no argumento que você fornecer a rsync . Eles não fazem parte de um comando shell nesse caso, apenas parte do valor expandido da substituição de comandos.

Por outro lado, se você escrever exclude_String+=(--exclude='/home/*') , agora as aspas são parte de um comando shell e são removidas durante o processamento desse comando. (Com o efeito de tornar * um caractere comum.)

rsync na verdade não espera ou processa cotações como parte do padrão de exclusão, ele apenas os tratará como qualquer outro caractere e excluirá arquivos com nomes contendo citações. Então, se você tem os padrões de exclusão como dados em algum arquivo, você não deve ter as citações lá.

Além disso, em vez de for x in $(cat file) , use while IFS= read -r line; do ...; done < file ou, neste caso, mapfile -t exclude_String < file . Essa substituição de comando com cat chama a divisão de palavras, portanto, você não pode ter padrões de exclusão contendo espaço em branco. Ele também invoca globbing dos padrões imediatamente, ali mesmo, na lista de palavras para o loop for . Não é isso que você quer.

Em geral, o que você precisa é:

excludes=()
while IFS= read -r line; do 
    excludes+=(--exclude="$line")
done < ./list 
rsync "$flags" "${excludes[@]}" . "$destination"

Com ./list contendo apenas os padrões a serem excluídos:

/home/*
*.vim*

(As aspas em torno de "$line" in +=(--exclude="$line") impedem que o shell gere arquivos chamados --exclude=something no diretório atual.)

Embora no caso de rsync , você também pode usar --exclude-from :

rsync "$flags" --exclude-from=./list . "$destination"

Se você for usar algo mais complicado que um arquivo para alimentar o loop, poderá usar a substituição de processo:

while IFS= read -r line; do
    excludes+=(--exclude="$line")
done < <(some command that produces the exclude patterns)

Veja também: Por que minha variável local está em um loop 'while read', mas não em outro loop aparentemente similar?

    
por 31.07.2018 / 23:48
0

Como ilkkachu explicou , seu problema é que as aspas simples não devem fazer parte dos padrões e que, se você escreve os padrões em um contexto de shell, o shell os removerá.

No entanto, parece que você pode ignorar a necessidade de ler seus padrões explicitamente em seu arquivo:

rsync --exclude-from=./list source/ target

Com --exclude-from=FILENAME , rsync lerá os padrões de exclusão do nome FILENAME . Observe que, ao fazer isso, os padrões não devem ter aspas simples neles.

    
por 01.08.2018 / 19:55