Lista de argumentos em apenas uma variável no bash

0

Existe um argumento de entrada com uma lista de arquivos (arquivos com curingas ou pastas) como este:

list="file1 dir1 **.data **.source"

Agora, cada elemento dessa lista deve ser prefixado com --filter=+ , transformando-o em uma lista de argumentos do comando rsync , parecendo algo assim:

args='--filter=="+ file1" --filter="+ dir1" --filter="+ **.data" --filter="+ **.source"'

Assim, ao passar $ args para rsync $args , ele deve receber cada argumento corretamente onde o espaço no "+" deve ser apenas um caractere do argumento e não um separador de dois argumentos.

Como fazer isso no script de shell (bash) usando apenas comandos de shell internos? Observe que o comando rsync deve receber cada argumento corretamente.

UMA TENTATIVA INCONSTITUCIONAL:

#!/bin/bash
set -f
list="file1 dir1 **.data **.source"
ct=0
arr=""
for i in $list; do
    ct=$(( ct + 1 ))
    arr[$ct]="--filter='+ $i'"
done
args=${arr[*]}

set -x
echo args:$args
rsync $args srcdir destdir
set +x

Quando ele é executado (supondo que a pasta srcdir exista no diretório atual), ele mostra:

$ ./test
+ echo args: '--filter='\''+' 'file1'\''' '--filter='\''+' 'dir1'\''' '--filter='\''+' '**.data'\''' '--filter='\''+' '**.source'\'''
args: --filter='+ file1' --filter='+ dir1' --filter='+ **.data' --filter='+ **.source'
+ rsync '--filter='\''+' 'file1'\''' '--filter='\''+' 'dir1'\''' '--filter='\''+' '**.data'\''' '--filter='\''+' '**.source'\''' a b
Unknown filter rule: ''+'
rsync error: syntax or usage error (code 1) at exclude.c(904) [client=3.1.1]
+ set +x

Veja que, embora echo mostre cada argumento corretamente:

args: --filter='+ file1' --filter='+ dir1' --filter='+ **.data' --filter='+ **.source'

O comando rsync parece não entender cada argumento corretamente, pois o "+" terminou o argumento e o espaço não fazia parte do argumento, mas um separador, corrompendo todos os argumentos.

    
por Luciano 20.03.2018 / 16:11

1 resposta

4

Em vez de criar a lista de nomes de arquivos e padrões como uma única string, você pode criar uma matriz para começar:

list=(file1 dir1 "**.data" "**.source")

e, em seguida, percorre os elementos:

args=()
for item in "${list[@]}" ; do
    args+=(--filter="+ $item")
done

Isso criaria argumentos como --filter=+ file1 , sem nenhuma aspas dentro da string de argumento. (Você não deseja que as aspas entrem em rsync . Ele reclamará de uma regra de filtro que as possui, por exemplo, rsync "--filter='+ foo'" ... )

E quando você passar a matriz para o comando, use "${args[@]}" para passar os elementos da matriz como strings distintas:

rsync "${args[@]}" "$srcdir" "$destdir"

Em vez de "--filter=+ foo" , acho que você poderia usar apenas --include=foo . Isso removeria um espaço problemático dos argumentos (mas não faria nada sobre espaços ou globs nos padrões de nome de arquivo).

No seu caso, você usou set -f para desabilitar a globbing, for i in $list deve funcionar, mas como você precisa de uma matriz, é melhor usar uma para começar.

Mais importante ainda, a atribuição args=${arr[*]} achata a matriz para uma única string. Agora os espaços dentro dos argumentos e os espaços entre os argumentos são iguais, há apenas --filter=+ file1 --filter=+ dir1 ... e a shell não tem como diferenciar os diferentes tipos de espaço em branco. A expansão sem aspas $args será dividida em todo e qualquer espaço em branco (que a saída set -x mostra, se você quiser analisar a confusão de aspas).

Com efeito, todos os ganhos de usar uma matriz foram discutidos nesta tarefa.

    
por 20.03.2018 / 16:27