Czego wymaga POSIX dla cytowanych tutaj dokumentów w ramach zastępowania poleceń?

20

W tym pytaniu ktoś zgłasza problem z użyciem dokumentu tutaj z cytowanym słowem ogranicznika w $(...)podstawianiu poleceń , gdzie odwrotny ukośnik \na końcu linii w dokumencie powoduje kontynuację linii łączącej nową linię , podczas gdy ten sam dokument tutaj podstawienie polecenia poza operacją działa zgodnie z oczekiwaniami .

Oto uproszczony przykładowy dokument:

cat <<'EOT'
abc ` def
ghi \
jkl
EOT

Obejmuje to jeden lewy i jeden lewy ukośnik na końcu linii. Separator jest cytowany, więc wewnątrz ciała nie występują żadne rozszerzenia. We wszystkich Bourne-alike'ach mogę znaleźć te dane wyjściowe dosłownie. Jeśli wstawię ten sam dokument do podstawienia polecenia w następujący sposób:

x=$(cat <<'EOT'
abc ` def
ghi \
jkl
EOT
)
echo "$x"

wtedy nie zachowują się już identycznie:

  • dash, ash, zsh, ksh93, BusyBox ash, mkshi SunOS 5.10 POSIX shwszystkim dać Verbatim zawartość dokumentu, jak poprzednio.
  • Bash 3.2 podaje błąd składniowy dla niedopasowanego backsticka. Przy dopasowanych backticksach próbuje uruchomić zawartość jako polecenie.
  • Bash 4.3 zwija „ghi” i „jkl” w jednym wierszu, ale nie ma błędu. --posixOpcja nie ma wpływu na to. Kusalananda mówi mi (dzięki!), Że pdkshzachowuje się tak samo .

W pierwotnym pytaniu powiedziałem, że to błąd w parserze Basha. Czy to jest [Aktualizacja: tak ] Odpowiedni tekst z POSIX (wszystkie z definicji Shell Command Language), który mogę znaleźć to:

  • §2.6.3 Zmiana polecenia :

    W formularzu $ (polecenie) wszystkie znaki następujące po otwartym nawiasie do pasującego nawiasu zamykającego stanowią polecenie. Do polecenia można użyć dowolnego poprawnego skryptu powłoki , z wyjątkiem skryptu składającego się wyłącznie z przekierowań, który daje nieokreślone wyniki.

  • §2.7.4 Dokument tutaj :

    Jeżeli cytowana jest jakakolwiek część słowa , separator należy utworzyć poprzez usunięcie cudzysłowu ze słowem , a wierszy dokumentu tutaj nie należy rozszerzać.

  • §2.2.1 Znak ucieczki (ukośnik odwrotny) :

    Jeśli <nowa linia> następuje po <lashlash>, powłoka interpretuje to jako kontynuację linii. <backlash> i <newline> należy usunąć przed podzieleniem danych wejściowych na tokeny.

  • §2.3 Rozpoznawanie tokenów :

    Kiedy gramatyka io_here zostanie rozpoznana przez gramatykę (patrz Gramatyka powłoki ), jeden lub więcej kolejnych wierszy bezpośrednio po następnym tokenie NEWLINE tworzy treść jednego lub więcej dokumentów tutaj i zostanie przeanalizowany zgodnie z zasadami zawartymi w niniejszym- dokument .

    Gdy nie przetwarza io_here , powłoka rozbija dane wejściowe na tokeny, stosując pierwszą obowiązującą regułę poniżej do następnego znaku na wejściu. ...

    ...

    1. Jeżeli bieżącym znakiem jest <odwrotny ukośnik>, pojedynczy cudzysłów lub podwójny cudzysłów i nie jest on cytowany, będzie to miało wpływ na cytowanie kolejnych znaków aż do końca cytowanego tekstu. Zasady cytowania są w sposób opisany w Cytowanie . Podczas rozpoznawania tokena nie dokonuje się w rzeczywistości żadnych podstawień, a token wynikowy powinien zawierać dokładnie znaki, które pojawiają się na wejściu (z wyjątkiem łączenia <nowej linii>), niezmodyfikowane, w tym wszelkie osadzone lub obejmujące cytaty lub operatory podstawienia, od końca do końca cytowanego tekstu.

Moją interpretacją tego jest to, że wszystkie znaki po, $(aż do zakończenia )zawierają skrypt powłoki, dosłownie; pojawia się dokument tutaj, więc przetwarzanie dokumentu tutaj następuje zamiast zwykłej tokenizacji; dokument tutaj ma następnie cudzysłów, co oznacza, że ​​jego treść jest przetwarzana dosłownie; i postać ucieczki nigdy do niej nie wchodzi. Widzę jednak argument, że ten przypadek po prostu nie został rozwiązany i oba zachowania są dopuszczalne. Możliwe, że pominąłem też jakiś odpowiedni tekst.


  • Czy ta sytuacja jest wyjaśniona gdzie indziej?
  • Na czym powinien polegać (w teorii) przenośny skrypt?
  • Czy szczególne traktowanie zapewniane przez którąkolwiek z tych powłok (Bash 3.2 / Bash 4.3 / wszyscy inni) jest wymagane przez standard? Zakazany? Dozwolony?
Michael Homer
źródło
Czy możesz nam pokazać, jak produkujesz swoją produkcję w drugim przypadku?
Julie Pelletier
@JuliePelletier echo "$x", ale jakikolwiek sposób sprawdzania zmiennej działa. Edytowałem tę linię na dole.
Michael Homer
2
Wygląda na to, że jest to łatwa naprawa. Ta poprawka wydaje się działać co najmniej: ignore_quoted_newline_in_quoted_heredoc.patch
geirha
1
Myślę, że interpretujesz to poprawnie i imo standard jest dość jasny, ponieważ „Powłoka rozszerzy podstawianie poleceń, wykonując polecenie w środowisku podpowłoki [...] i zastępując podstawienie polecenia [...] standardowym wyjściem polecenie [...] " Więc uruchamia polecenie w podpowłoce i zastępuje $(...)czymkolwiek to wyjście ... Teraz, uruchamiając polecenie w twoim przykładzie w podpowłoce (in bash), generuje oczekiwany wynik. Dopiero po przekształceniu go w substytucję polecenia zwija „ghi” i „jkl”. To jest błąd imo
don_crissti
2
@geirha Zgłosiłem błąd Bash ; Nie zamierzam niepokoić się pdksh, ponieważ wydaje się, że nie ma on nawet cienia bieżącej konserwacji.
Michael Homer,

Odpowiedzi:

5

Zapytano o to na liście mailingowej Basha, a opiekun potwierdził, że to błąd

Wspomnieli również, że tekst w POSIX „niekoniecznie jest dwuznaczny, ale wymaga dokładnej lektury.”, Więc poprosiłem o wyjaśnienie tego. Odpowiedzi, w tym opis problemu i interpretacja normy, były następujące:

Podstawienie polecenia to czerwony śledź; jest istotny tylko dlatego, że wskazał, gdzie był błąd.

Ogranicznik do dokumentu tutaj jest cytowany, więc wiersze nie są rozwijane. W takim przypadku powłoka odczytuje wiersze z danych wejściowych tak, jakby były cytowane. Jeśli odwrotny ukośnik pojawia się w kontekście, w którym jest cytowany, nie działa on jako znak zmiany znaczenia (patrz poniżej), a specjalna obsługa odwrotnego ukośnika-nowa linia nie ma miejsca. W rzeczywistości, jeśli cytowana jest jakakolwiek część separatora, wiersze dokumentu tutaj są odczytywane tak, jakby były pojedynczymi cudzysłowami.

Tekst w Posix 2.2.1 jest napisany niezręcznie, ale oznacza, że ​​odwrotny ukośnik jest traktowany specjalnie, gdy nie jest cytowany. Możesz zacytować ukośnik odwrotny i zahamować całe rozwijanie tylko za pomocą pojedynczych cudzysłowów lub innego ukośnika odwrotnego.

Bliższą częścią do przeczytania jest „nierozwinięty” tekst sugerujący pojedyncze cudzysłowy. Standard mówi w 2.2, że tutaj dokumenty są „inną formą cytowania”, ale jedyną formą cytowania, w której słowa nie są w ogóle rozwinięte, są pojedyncze cytaty. Jest to więc forma cytowania, która jest dokładnie taka sama jak pojedyncze cytaty, ale nie pojedyncze.

Kevin
źródło
@Scott (1) Wierzę, że to odpowiada na wszystkie pytania i nic nie jest zbędne. Mój komentarz, który zaczyna się od odpowiedzi, dotyczy usunięcia dokonanego przez moderatora, który źle zrozumiał sytuację. (2) Nie mam wystarczającej reputacji. (3) Byłbym wdzięczny za podobne zachowanie osób usuwających moje odpowiedzi, ale z pewnością będę o tym pamiętać w przyszłości. Dzięki za myśli.
Kevin,
Chodzi mi o to, że większość twojego pierwszego akapitu to rozmowa z Michałem Mrożkiem, a nie odpowiedź na pytanie. Zdaję sobie sprawę, że nie masz wystarczającej reputacji, aby skomentować dowolny post, ale uważam, że masz dość na meta i czat.
Scott,
1
@Scott Rozumiem i doceniam, że próbujesz usprawnić odpowiedź, ale wcześniej opublikowałem tę dokładnie usprawnioną odpowiedź (tylko cytat i link do niej), i został on usunięty przez moderatora (bez dyskusji!) I ja w usuniętym poście nie ma żadnych linków do czatu i kwestionowania tej decyzji. Miałem nadzieję, że odpowiadając na jego bezpodstawną krytykę, przetrwa usunięcie, zostanie zaakceptowany przez pytającego, a następnie zmodyfikuję odpowiedź, aby usunąć preambułę.
Kevin,