Łączenie ciągu Bash używane do budowania listy parametrów

12

Biorąc pod uwagę ten bash:

PARMS='-rvu'
PARMS+=" --delete --exclude='.git'"
echo $PARMS
rsync ${PARMS} . ${TARGET}

Echo pokazuje ciąg PARMS zgodnie z oczekiwaniami, nie wyświetla się żaden błąd, ale rsync cicho działa tak, jakby opcje dodane przez + = nie istniały. Działa to jednak zgodnie z oczekiwaniami:

PARMS='-rvu'
rsync ${PARMS} --delete --exclude='.git' . ${TARGET}

Chyba spieprzyłem coś z cytatami bashowymi (zawsze miałem z tym problemy), ale nie jestem pewien, co i dlaczego opcje są ignorowane, nawet jeśli łańcuch wydaje się być poprawnie zbudowany.

neuviemeporte
źródło
1
echo "$PARMS"i rsync "${PARMS}"...
jasonwryan
Działa to dla mnie w bashwersji 4.2.25 bez żadnych zmian.
Anthon

Odpowiedzi:

17

Istnieje różnica między:

PARMS+="... --exclude='.git'"

i

... --exclude='.git'

W pierwszym, pojedyncze cytaty są same w sobie, więc dosłownie są obecne w zastąpionym tekście podanym rsyncjako argument. rsyncdostaje argument, którego wartość wynosi --exclude='.git'. W drugim przypadku pojedyncze cudzysłowy są interpretowane przez powłokę w momencie ich pisania, ponieważ same nie są wewnątrz cudzysłowów i rsyncmożna je zobaczyć --exclude=.git.

W tym przypadku nie potrzebujesz w ogóle pojedynczych cudzysłowów - .gitto samo w sobie poprawne słowo powłoki, bez znaków specjalnych, więc możesz używać go dosłownie w poleceniu.

Jednak dla tego rodzaju rzeczy lepsza jest tablica :

PARMS=(-rvu)
PARMS+=(--delete --exclude='.git')
rsync "${PARMS[@]}"

Tworzy to twoje polecenie jako osobne słowa, z dowolnym cytatem, który chcesz interpretować w momencie pisania wiersza tablicy. "${PARMS[@]}"rozwija się do każdego wpisu w tablicy jako osobny argument, nawet jeśli sam argument zawiera w sobie znaki specjalne lub spacje, więc rsyncwidzisz, co napisałeś tak, jak chciałeś.

Michael Homer
źródło
bashwykonano dzielenie słów po ${PARMS}rozszerzeniu. Więc pojedynczy cytat został również zinterpretowany przez powłokę.
cuonglm
2
Spróbuj! Zrobiłem. Cytaty pozostają, a jeśli między nimi są spacje, to i tak są to punkty podziału.
Michael Homer
@Gnouc: Od strony atakujących człowieka: „Cytat: Usuwanie Po poprzednich rozszerzeń, wszystkie nienotowane wystąpień znaków \ , 'i "., Które nie wynikają z jednego z powyższych rozszerzeń są usuwane” „powyżej rozszerzeń” obejmuje rozszerzenie parametrów, które wykonuje rozszerzenie ${PARMS}.
camh
Dzięki. Rozumiem więc, że w takim przypadku pominięcie pojedynczych cudzysłowów w podwójnych będzie działać, ale ze względu na kompletność - co jeśli będę musiał zacytować jakieś znaki specjalne i nie chciałbym zastosować twojego drugiego podejścia?
neuviemeporte
Jeśli twoje znaki specjalne nie są częścią IFS(zazwyczaj spacją), nie musisz ich cytować. Jeśli tak, to masz pecha, chyba że zhakujesz coś razem eval- to ogólnie jest trochę mylne, a tablice są właściwym sposobem na poradzenie sobie z tym.
Michael Homer
2

Oprócz odpowiedzi @Michael Homer , możesz użyć bash funkcji eval :

PARMS='-rvu'
PARMS+=" --delete --exclude='.git'"
echo "$PARMS"
eval "rsync ${PARMS} . "'"${TARGET}"'
Cuonglm
źródło
2
Możesz, ale nie powinieneś. Tablice zostały dodane specjalnie, aby uniknąć tego użycia eval.
chepner