Jak używać zmiennej środowiskowej wewnątrz cytowanego ciągu w Bash

96

Wypróbowałem różne formy następujących w skrypcie bash:

#!/bin/bash
svn diff $@ --diff-cmd /usr/bin/diff -x "-y -w -p -W $COLUMNS"

Ale nie mogę uzyskać składni, aby poprawnie rozwinąć COLUMNSzmienną środowiskową.

Próbowałem różnych form:

svn diff $@ --diff-cmd /usr/bin/diff -x '-y -w -p -W $COLUMNS'

i

svn diff $@ --diff-cmd /usr/bin/diff -x '-y -w -p -W ${COLUMNS}'

i

eval svn diff $@ --diff-cmd /usr/bin/diff -x "-y -w -p -W $COLUMNS"

Propozycje?

Jamie
źródło
więc co te przykłady dają w twoim przypadku? A co chcesz, żeby wyprodukowali?
polecenie poza skryptem działa?
dfa
Próbowałeśsvn diff $@ --diff-cmd /usr/bin/diff -x "-y -w -p -W ""$COLUMNS"
Qian Chen
BTW, użycie $@niecytowanych sprawia, że ​​jest to dokładnie to samo, co $*- co oznacza, że ​​dzieli się "foo bar"na dwa oddzielne argumenty, fooi bar. Jeśli chcesz zachować oryginalną listę argumentów w takiej postaci, w jakiej została ci ona podana, chcesz "$@".
Charles Duffy

Odpowiedzi:

17

Jeśli nie masz pewności, możesz użyć żądania „cols” na terminalu i zapomnieć o COLUMNS:

COLS=$(tput cols)
TheBonsai
źródło
Z jakiegoś powodu jest to jedyne rozwiązanie, które udało mi się uzyskać, pracując poza tą stroną (stan na dzień 11 / maja 10:38 ET). Dzięki
Jamie
408

Tylko krótka notatka / podsumowanie dla każdego, kto przyszedł tutaj przez Google, szukając odpowiedzi na ogólne pytanie zadane w tytule (tak jak ja). Aby uzyskać dostęp do zmiennych powłoki wewnątrz cudzysłowów, powinno działać dowolne z poniższych:

echo "$VARIABLE"
echo "${VARIABLE}"

Głównym problemem jest stosowanie pojedynczych cudzysłowów. Zgodnie z podręcznikiem Bash Reference Manual :

Ujmowanie znaków w pojedyncze cudzysłowy ( ') zachowuje dosłowną wartość każdego znaku w cudzysłowie. Pojedynczy cudzysłów nie może występować między pojedynczymi cudzysłowami, nawet jeśli jest poprzedzony odwrotnym ukośnikiem. [...] obudów znaków w cudzysłowach ( ") chroni dosłowne wartości wszystkich znaków w cudzysłowie, z wyjątkiem $, `, \i, gdy ekspansja historia jest włączona !. Znaki $i `zachowują swoje specjalne znaczenie w cudzysłowach (zobacz Rozszerzenia powłoki). Odwrotny ukośnik zachowuje specjalne znaczenie tylko wtedy, gdy następuje jedno z następujących znaków: $, `, ",\lub nowa linia. W podwójnych cudzysłowach usuwane są ukośniki odwrotne, po których następuje jeden z tych znaków. Ukośniki odwrotne poprzedzające znaki bez specjalnego znaczenia pozostają niezmienione. Podwójny cudzysłów można cytować w cudzysłowie, poprzedzając go lewym ukośnikiem. Jeśli ta opcja jest włączona, !interpretacja historii będzie wykonywana, chyba że znak znajdujący się w cudzysłowie zostanie zastąpiony lewym ukośnikiem. Odwrotny ukośnik poprzedzający !nie jest usuwany. Parametry specjalne *i @mają specjalne znaczenie, gdy są umieszczone w cudzysłowach (zobacz Rozszerzanie parametrów powłoki).

W konkretnym przypadku zadanym w pytaniu $ COLUMNS jest specjalną zmienną, która ma niestandardowe właściwości (patrz odpowiedź lhunatha powyżej).

RekurencyjnieIronic
źródło
20
W przypadku pojedynczych cudzysłowów może to obejście pomoże: 'before'"$variable"'after'(jak podano w tej odpowiedzi: stackoverflow.com/a/13802438/2254346 )
MakisH,
Świetna odpowiedź! Miałem ten sam problem z odniesieniem mojej zmiennej env w pojedynczych cudzysłowach. Kiedy zmieniłem pojedynczy cudzysłów na cudzysłowy, moja zmienna env była rozszerzana zgodnie z oczekiwaniami.
Binita Bharati
14

Zauważ, że COLUMNSto:

  1. NIE zmienna środowiskowa. Jest to zwykły parametr bash, który jest ustawiany przez sam bash.
  2. Ustawiany automatycznie po otrzymaniu SIGWINCHsygnału.

Ten drugi punkt zwykle oznacza, że COLUMNSzmienna zostanie ustawiona tylko w powłoce interaktywnej , a nie w skrypcie bash.

Jeśli twój skrypt stdinjest podłączony do twojego terminala, możesz ręcznie sprawdzić szerokość terminala, pytając terminal:

tput cols

Aby użyć tego w poleceniu SVN:

svn diff "$@" --diff-cmd /usr/bin/diff -x "-y -w -p -W $(tput cols)"

(Uwaga: należy cytować "$@" i trzymać się z daleka eval;-))

lhunath
źródło
Dzięki za informację; Wiedza o tym, co się dzieje, nadaje temu zagadnieniu poczucie katharsis.
Jamie
2

Poniższy skrypt działa dla mnie dla wielu wartości $COLUMNS. Zastanawiam się, czy nie siedzisz COLUMNSprzed tym wezwaniem?

#!/bin/bash
COLUMNS=30
svn diff $@ --diff-cmd /usr/bin/diff -x "-y -w -p -W $COLUMNS"

Czy możesz wywołać echo $COLUMNSw swoim skrypcie, aby sprawdzić, czy jest ustawiony poprawnie?

Alex B.
źródło
Boo, syk re: unquoted $@- to dzieli pojedynczy parametr "two words"na dwa oddzielne parametry twoi words. Powinien "$@"przekazywać listę argumentów dokładnie tak, jak została podana.
Charles Duffy
0

Robisz to dobrze, więc myślę, że coś innego jest winne (nie eksportujesz KOLUMN?).

Sztuczka do debugowania tych przypadków polega na wykonaniu wyspecjalizowanego polecenia (zamknięcie dla facetów od języków programowania). Utwórz skrypt powłoki o nazwie diff-columns, wykonując:

exec /usr/bin/diff -x -y -w -p -W "$COLUMNS" "$@"

i po prostu użyj

svn diff "$@" --diff-cmd  diff-columns

W ten sposób twój kod jest bardziej czytelny i bardziej modularny (podejście z góry na dół), a także możesz przetestować kod z kolumnami różnicowymi dokładnie oddzielnie (podejście oddolne).

Colas Nahaboo
źródło
Linia poleceń jest analizowana przed rozwidleniem procesu, więc nie eksportowanie KOLUMN nie może być problemem.
Peter van der Heijden
1
Mówi, że jego kod jest częścią skryptu bash, więc może to być problem.
Colas Nahaboo