Jak używać zmiennych w pojedynczych cudzysłowach

11

Mam aplikację, która przyjmuje jako atrybuty wejściowe w podwójnych cudzysłowach osadzonych w pojedynczych cudzysłowach. Weźmy na przykład to właściwe polecenie:

command -p 'cluster="cl1"'

Aby to zautomatyzować, stworzyłem plik bash, używając go $CLUSTERjako zmiennej. Jak powinno być moje polecenie? Innymi słowy, co powinienem umieścić zamiast cl1?

Pamiętaj, że jeśli zmodyfikuję powyższe polecenie, nie zostanie zaakceptowane. Na przykład: command -p "cluster=cl1"nie jest akceptowane

Mohamad-Jaafar NEHME
źródło
2
CLUSTER='"cl1"'; command -p "cluster=$CLUSTER"
mikeserv
Inna możliwość (jeśli wolisz przechowywać clibez cudzysłowów w CLUSTERzmiennej):CLUSTER='cl1'; command -p 'cluster="'"$CLUSTER"'"'
jimmij
Wreszcie znalazłem właściwą odpowiedź. Dzięki @jimmij.
Mohamad-Jaafar NEHME

Odpowiedzi:

8

Wygląda na to, że twoje polecenie może ustawiać zmienne środowiskowe na podstawie argumentów podanych w wierszu poleceń. Być może możesz zrobić:

CLUSTER=cl1; cluster=$CLUSTER command

... i ustaw dla niego swoje środowisko przy wywołaniu.

W przeciwnym razie cudzysłowy powłoki zwykle ograniczają argumenty lub unikają innych specjalnych znaków powłoki przed interpretacją powłoki. Możesz zawierać (i dlatego uciec) różne rodzaje cudzysłowów powłoki w obrębie innych rodzajów w oparciu o różne reguły:

  • "''''" - ciąg o miękkich cytatach może zawierać dowolną liczbę twardych cytatów.
  • "\""- \ukośnik odwrotny może uciec od "miękkiego cytatu w ciągu "miękko cytowanego.
    • W tym kontekście \\odwrotny ukośnik również ucieka, \$token rozwinięcia i \newline jak wspomniano poniżej, ale poza tym jest traktowany dosłownie.
  • "${expand} and then some"- łańcuch z miękkimi cudzysłowami może zawierać interpretowane $rozwinięcie powłoki .
  • '"\'- 'ciąg znaków cytowanych na stałe może zawierać dowolny znak inny niż 'ciągły cytat.
  • \- niecytowany odwrotny ukośnik ucieknie od dowolnego następującego znaku dla dosłownej interpretacji - nawet innego odwrotnego ukośnika - z wyjątkiem \newline.
    • W \\nprzypadku ewline \odwrotny ukośnik i \newline są całkowicie usuwane z wynikowej interpretowanej komendy.
  • ${parameter+expand "$parameter"}- cytaty wynikające z rozszerzenia powłoki prawie nigdy nie służą jako znaczniki separatora, z wyjątkiem kilku specjalnych przypadków. Nie zaryzykuję opisania ich tutaj.

Uważam za dziwne, że jakakolwiek aplikacja interpretuje cudzysłowy w argumentach wiersza poleceń. Taka praktyka nie ma większego sensu w tym, że - przynajmniej w przypadku powłok - głównym celem cytatu jest na ogół rozgraniczenie argumentu. Przy wywołaniu jednak argumenty zawsze są już ograniczone ze \0NULznaków, a więc cytat nie może służyć wiele celów.

Nawet powłoka zwykle z trudem interpretuje cudzysłowy w jednym z argumentów wywołania, gdy jest wywoływana -cprzełącznikiem - co oznacza, że ​​jej pierwszy operand jest tak naprawdę skryptem powłoki, który powinien uruchomić przy wywołaniu. Jest to przypadek dwukrotnej oceny danych wejściowych.

To powiedziawszy, możesz zrobić wiele rzeczy, aby przekazać dosłowne cudzysłowy za pomocą argumentów w wierszu poleceń. Na przykład:

CLUSTER='"cl1"'; command -p "cluster=$CLUSTER"

Jak zauważyłem w komentarzu wcześniej, możesz zawierać "cytaty w rozwinięciu, które jest "cytowane.

CLUSTER=cl1; command -p "cluster=\"$CLUSTER\""

Możesz uciec "z \ukośnikiem odwrotnym w "cytowanym ciągu.

CLUSTER=cl1; command -p cluster='"'"$CLUSTER"'"'

Możesz zamieniać i łączyć style cytowania, aby osiągnąć pożądany efekt końcowy, jak w notatkach @jimmij powyżej .

CLUSTER=cl1; ( set -f; IFS=; command -p cluster=\"$CLUSTER\" )

Możesz wyłączyć zarówno generowanie, jak i $IFSdzielenie nazw plików - unikając w ten sposób konieczności cytowania $expansion- a więc tylko cytowania. To prawdopodobnie przesada.

Na koniec można użyć innego rodzaju cytowania powłoki. Jak zauważyłem wcześniej, sh -c "$scriptlet"forma wywoływania powłoki jest często używana do zapewnienia skryptu powłoki w wierszu poleceń. Kiedy $scriptletsię komplikuje - na przykład gdy cytaty muszą zawierać inne cytaty - często korzystne może być użycie dokumentu tutaj, a sh -szamiast tego - gdy powłoka jest specjalnie instruowana, aby przypisać wszystkie następujące argumenty do parametrów pozycyjnych, tak jak w -cprzypadku i jeszcze do wzięcia skryptu stdin.

Jeśli twoje polecenie musi interpretować cytaty w ten sposób, uważam, że lepiej byłoby, gdyby mógł to zrobić w pliku wejściowym. Na przykład:

CLUSTER=cl1
command --stdin <<-SCRIPT
    cluster="$CLUSTER"
SCRIPT

Jeśli nie cytujesz separatora <<here-documenta, cała jej zawartość jest traktowana prawie dokładnie tak, jak w "cudzysłowie - z tym wyjątkiem, że "same cudzysłowy nie są traktowane specjalnie. Jeśli więc uruchomimy powyższe z cat:

CLUSTER=cl1
cat <<-SCRIPT
        cluster="$CLUSTER"
SCRIPT

... drukuje ...

cluster="cl1"
mikeserv
źródło
1

Jak napisał Mikeserv :

 CLUSTER='"cl1"'; command -p "cluster=$CLUSTER" 

„Double cytat” każdy dosłowne, że zawiera spacje / metaznaki i każdy rozbudowa: "$var", "$(command "$var")", "${array[@]}", "a & b". Użyj 'single quotes'kodu lub dosłowny $'s: 'Costs $5 US', ssh host 'echo "$HOSTNAME"'. Zobacz
http://mywiki.wooledge.org/Quotes
http://mywiki.wooledge.org/Arguments
http://wiki.bash-hackers.org/syntax/words

Gilles Quenot
źródło
Masz rację, dziękuję. A co z automatyzacją? Załóżmy, że chcę przeczytać CLUSTER?
Utknę
@Moi - automatyzacja nie jest wielkim problemem, choć będziesz musiał zdezynfekować zmienną "znaków - tylko pierwsza i ostatnia powinna wystarczyć. Prawdziwy problem polega na tym, że Twoja aplikacja interpretuje cytaty w arg. Interpretacja cytatu w argumencie prawie nigdy nie jest dobrym pomysłem, ponieważ cytat powinien być jedynie środkiem do rozgraniczenia argumentu - i przy wywołaniu, separacja jest obsługiwana przez \0NULs w każdym przypadku. Prawdopodobnie istnieje lepszy sposób na przekazanie informacji, które chcesz do aplikacji? Podoba ci się command --'script=/path/to/some/file'?
mikeserv