POSIX yêu cầu gì khi trích dẫn ở đây các tài liệu bên trong thay thế lệnh?


20

Trong câu hỏi này, một người nào đó báo cáo sự cố khi sử dụng tài liệu ở đây với một từ phân cách được trích dẫn bên trong $(...)thay thế lệnh , trong đó dấu gạch chéo ngược \ở cuối dòng bên trong tài liệu kích hoạt tiếp tục dòng nối dòng mới , trong khi đó tài liệu bên ngoài thay thế lệnh hoạt động như mong đợi .

Đây là một tài liệu ví dụ đơn giản:

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

Điều này bao gồm một backtick và một dấu gạch chéo ngược ở cuối dòng. Dấu phân cách được trích dẫn, vì vậy không có sự mở rộng nào xảy ra bên trong cơ thể. Trong tất cả các Bourne-alike tôi có thể tìm thấy kết quả này nguyên văn nội dung. Nếu tôi đặt cùng một tài liệu bên trong lệnh thay thế như sau:

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

sau đó họ không còn cư xử giống hệt nhau:

  • dash, ash, zsh, ksh93, BusyBox ash, mkshvà SunOS 5.10 POSIX shtất cả các cung cấp cho các nội dung nguyên văn của tài liệu, như trước đây.
  • Bash 3.2 đưa ra một lỗi cú pháp cho một backtick chưa từng có. Với backticks phù hợp, nó cố gắng chạy nội dung dưới dạng một lệnh.
  • Bash 4.3 thu gọn "ghi" và "jkl" vào một dòng, nhưng không có lỗi. Các --posixtùy chọn không ảnh hưởng này. Kusalananda nói với tôi (cảm ơn!) Hành pdkshxử theo cùng một cách .

Trong câu hỏi ban đầu, tôi đã nói rằng đây là một lỗi trong trình phân tích cú pháp của Bash. Là nó? [Cập nhật: ] Văn bản có liên quan từ POSIX (tất cả từ định nghĩa Ngôn ngữ lệnh Shell) mà tôi có thể tìm thấy là:

  • §2.6.3 Thay thế lệnh :

    Với biểu mẫu $ (lệnh), tất cả các ký tự theo dấu ngoặc đơn mở cho dấu ngoặc đơn đóng phù hợp sẽ tạo thành lệnh. Bất kỳ tập lệnh shell hợp lệ nào cũng có thể được sử dụng cho lệnh , ngoại trừ tập lệnh chỉ bao gồm các chuyển hướng tạo ra kết quả không xác định.

  • §2.7.4 Dưới đây - Tài liệu :

    Nếu bất kỳ phần nào của từ được trích dẫn, dấu phân cách sẽ được hình thành bằng cách thực hiện loại bỏ trích dẫn trên từ và các dòng tài liệu ở đây sẽ không được mở rộng.

  • §2.2.1 Ký tự thoát (Dấu gạch chéo ngược) :

    Nếu <newline> tuân theo <dấu gạch chéo ngược>, trình bao sẽ hiểu điều này là tiếp tục dòng. <Backslash> và <newline> sẽ bị xóa trước khi chia đầu vào thành các mã thông báo.

  • §2.3 Nhận dạng mã thông báo :

    Khi mã thông báo io_here đã được ngữ pháp nhận ra (xem Ngữ pháp Shell ), một hoặc nhiều dòng tiếp theo ngay sau mã thông báo NEWLINE tiếp theo tạo thành cơ thể của một hoặc nhiều tài liệu ở đây và sẽ được phân tích cú pháp theo các quy tắc tại đây- Tài liệu .

    Khi nó không xử lý io_here , shell sẽ phá vỡ đầu vào của nó thành các mã thông báo bằng cách áp dụng quy tắc áp dụng đầu tiên bên dưới cho ký tự tiếp theo trong đầu vào của nó. ...

    ...

    1. Nếu ký tự hiện tại là <backslash>, trích dẫn đơn hoặc trích dẫn kép và nó không được trích dẫn, nó sẽ ảnh hưởng đến trích dẫn cho các ký tự tiếp theo cho đến cuối văn bản được trích dẫn. Các quy tắc để trích dẫn được mô tả trong Trích dẫn . Trong quá trình nhận dạng mã thông báo, không có sự thay thế nào thực sự được thực hiện và mã thông báo kết quả sẽ chứa chính xác các ký tự xuất hiện trong đầu vào (ngoại trừ tham gia <newline>), không được sửa đổi, bao gồm mọi trích dẫn được nhúng hoặc kèm theo hoặc giữa các toán tử thay thế, giữa và cuối của văn bản trích dẫn.

Giải thích của tôi về điều này là tất cả các nhân vật sau $(khi kết thúc )bao gồm tập lệnh shell, nguyên văn; một tài liệu ở đây xuất hiện, vì vậy ở đây - quá trình xử lý tài liệu xảy ra thay vì mã thông báo thông thường; tài liệu ở đây sau đó có một dấu phân cách được trích dẫn, có nghĩa là nội dung của nó được xử lý nguyên văn; và nhân vật thoát không bao giờ đi vào nó. Tôi có thể thấy một đối số, tuy nhiên, trường hợp này đơn giản là không được giải quyết và cả hai hành vi đều được cho phép. Có thể tôi cũng đã bỏ qua một số văn bản có liên quan ở đâu đó.


  • Là tình huống này được làm rõ ràng hơn ở nơi khác?
  • Một kịch bản di động nên có thể dựa vào (về lý thuyết) là gì?
  • Là điều trị cụ thể được đưa ra bởi bất kỳ trong số các vỏ này (Bash 3.2 / Bash 4.3 / mọi người khác) có bắt buộc theo tiêu chuẩn không? Cấm? Được phép?

Bạn có thể chỉ cho chúng tôi cách bạn sản xuất đầu ra của mình trong trường hợp thứ hai không?
Julie Pelletier

@JuliePelletier echo "$x", nhưng bất kỳ cách nào để kiểm tra biến đều hoạt động. Tôi đã chỉnh sửa dòng đó vào dưới cùng.
Michael Homer

2
Có vẻ như đó là một sửa chữa dễ dàng. Vá này dường như làm việc ít nhất: ignore_quoted_newline_in_quoted_heredoc.patch
geirha

1
Tôi nghĩ rằng bạn đang giải thích điều này một cách chính xác và imo tiêu chuẩn khá rõ ràng vì "Shell sẽ mở rộng thay thế lệnh bằng cách thực thi lệnh trong môi trường lớp con [...] và thay thế lệnh thay thế [...] bằng đầu ra tiêu chuẩn của lệnh [...] " Vì vậy, nó chạy lệnh trong một lớp con và thay thế $(...)bằng bất cứ thứ gì mà đầu ra là ... Bây giờ, khi chạy lệnh trong ví dụ của bạn trong một lớp con (in bash), nó sẽ tạo ra kết quả mong đợi. Chỉ khi biến nó thành sự thay thế lệnh thì nó mới thu gọn "ghi" và "jkl". Vì vậy, đây là một lỗi imo
don_crissti

2
@geirha Tôi đã báo cáo lỗi Bash ; Tôi sẽ không bận tâm về pdksh vì nó dường như không có bóng của bảo trì hiện tại.
Michael Homer

Câu trả lời:


5

Điều này đã được hỏi trong danh sách gửi thư của Bash và người bảo trì xác nhận đó là một lỗi

Họ cũng đề cập rằng văn bản trong POSIX "không nhất thiết là mơ hồ, nhưng nó đòi hỏi phải đọc kỹ.", Vì vậy tôi đã yêu cầu làm rõ về điều đó. Câu trả lời của họ bao gồm mô tả về vấn đề và giải thích tiêu chuẩn như sau:

Thay thế lệnh là một cá trích đỏ; Nó chỉ liên quan ở chỗ nó chỉ ra lỗi ở đâu.

Dấu phân cách cho tài liệu ở đây được trích dẫn, vì vậy các dòng không được mở rộng. Trong trường hợp này, shell đọc các dòng từ đầu vào như thể chúng được trích dẫn. Nếu dấu gạch chéo ngược xuất hiện trong ngữ cảnh được trích dẫn, thì nó không hoạt động như một ký tự thoát (xem bên dưới) và việc xử lý đặc biệt của dấu gạch chéo ngược mới không diễn ra. Trong thực tế, nếu bất kỳ phần nào của dấu phân cách được trích dẫn, thì các dòng tài liệu ở đây được đọc như thể trích dẫn đơn.

Văn bản trong Posix 2.2.1 được viết lúng túng, nhưng có nghĩa là dấu gạch chéo ngược chỉ được xử lý đặc biệt khi nó không được trích dẫn. Bạn có thể trích dẫn dấu gạch chéo ngược và ức chế tất cả sự mở rộng chỉ bằng dấu ngoặc đơn hoặc dấu gạch chéo ngược khác.

Phần đọc gần là văn bản "không mở rộng" ngụ ý các trích dẫn đơn. Tiêu chuẩn nói trong 2.2 rằng ở đây các tài liệu là "một hình thức trích dẫn khác", nhưng hình thức trích dẫn duy nhất trong đó các từ không được mở rộng hoàn toàn là các trích dẫn. Vì vậy, đây là một hình thức trích dẫn gần giống như trích dẫn đơn, nhưng không phải là trích dẫn đơn.


@Scott (1) Tôi tin rằng điều này trả lời tất cả các câu hỏi và không có gì là thừa. Nhận xét của tôi bắt đầu câu trả lời liên quan đến việc xóa bởi một người điều hành đã hiểu sai tình huống. (2) Tôi không có đủ danh tiếng. (3) Tôi sẽ đánh giá cao hành vi tương tự bằng cách xóa những câu trả lời của tôi, nhưng tôi chắc chắn sẽ ghi nhớ điều đó trong tương lai. Cảm ơn những suy nghĩ.
Kevin

Quan điểm của tôi là hầu hết đoạn đầu tiên của bạn là một cuộc trò chuyện với Michael Mrozek và không phải là một câu trả lời cho câu hỏi. Tôi nhận ra rằng bạn không có đủ danh tiếng để bình luận về bất kỳ bài đăng nào, nhưng tôi tin rằng bạn có đủ cho meta và trò chuyện.
Scott

1
@ Hủy bỏ Tôi hiểu và đánh giá cao rằng bạn đang cố gắng sắp xếp câu trả lời, nhưng tôi đã đăng câu trả lời được sắp xếp chính xác trước đó (chỉ trích dẫn và liên kết đến nó) và nó đã bị xóa bởi người điều hành thấy không có liên kết trong bài viết bị xóa để trò chuyện và tranh chấp quyết định đó. Tôi hy vọng rằng bằng cách trả lời những lời chỉ trích vô căn cứ của mình, nó sẽ tồn tại trong việc xóa, được người hỏi chấp nhận và sau đó tôi sẽ sửa đổi câu trả lời để loại bỏ lời mở đầu.
Kevin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.