Tại sao dấu gạch ngang mở rộng \ \ \ khác với bash?


7

Tôi có một dự án nguồn mở nhỏ mà vì nhiều lý do tôi đã cố gắng viết bằng kịch bản shell di động hợp lý. Các kiểm tra tích hợp tự động của nó kiểm tra các ký tự thù địch trong các biểu thức đường dẫn được xử lý đúng cách, trong số các thứ khác.

Người dùng /bin/shđược cung cấp bởi bashđang thấy lỗi trong bài kiểm tra mà tôi đã đơn giản hóa như sau:

echo "A bug\\'s life"
echo "A bug\\\\'s life"

Trên bash, nó tạo ra kết quả mong đợi này:

A bug\'s life
A bug\\'s life

Với dấu gạch ngang, mà tôi đã phát triển để chống lại, nó thực hiện điều này:

A bug\'s life
A bug\'s life

Tôi muốn nghĩ rằng tôi đã không tìm thấy một lỗi trong dấu gạch ngang, rằng tôi có thể đang thiếu một cái gì đó thay vào đó. Có một lời giải thích hợp lý cho điều này?



2
Điều đó khá hữu ích. Mặc dù nó không bao gồm trực tiếp trình tự \ \, nhưng nó giải thích lý do tại sao mọi thứ có thể theo cách của chúng trong dấu gạch ngang và chỉ ra giải pháp: sử dụng printf, dễ di chuyển hơn trong xử lý các chuỗi thoát. Cảm ơn
csirac2

1
Nó nên là tại sao đầu ra bash khác với bash? . Đây là phiên bản dash là tiêu chuẩn ở đây.
Stéphane Chazelas

Câu trả lời:


11

Trong

echo "A bug\\'s life"

Bởi vì đó là dấu ngoặc kép và \đặc biệt bên trong dấu ngoặc kép, cái đầu tiên \được hiểu bởi shell là thoát / trích dẫn cái thứ hai \. Vì vậy, một A bug\'s lifecuộc tranh cãi đang được thông qua echo.

echo "A bug\'s life"

Sẽ đạt được chính xác như nhau. 'không đặc biệt trong dấu ngoặc kép, \không bị xóa vì vậy đó là đối số chính xác được truyền cho echo.

Như đã giải thích tại sao printf tốt hơn echo? , có rất nhiều biến thể giữa các lần echothực hiện.

Trong các triển khai tuân thủ Unix như dash's, \được sử dụng để giới thiệu các chuỗi thoát: \ncho dòng mới, \bcho backspace, \0123cho các chuỗi bát phân ... và \\cho chính dấu gạch chéo ngược.

Một số (không phải POSIX) yêu cầu -etùy chọn cho điều đó hoặc chỉ thực hiện khi ở chế độ tuân thủ (như bashkhi được xây dựng với các tùy chọn phù hợp như cho shOS / X hoặc khi được gọi SHELLOPTS=xpg_echotrong môi trường).

Vì vậy, trong tiêu chuẩn (chỉ tiêu chuẩn Unix; POSIX để lại hành vi không xác định) echos,

echo '\\'

giống như:

echo "\\\\"

xuất ra một dấu gạch chéo ngược, trong bashkhi ở chế độ không tuân thủ:

echo '\\'

sẽ xuất ra hai dấu gạch chéo ngược.

Tốt nhất nên tránh echovà sử dụng printfthay thế:

$ printf '%s\n' "A bug\'s life"
A bug\'s life

Mà hoạt động như nhau trong trường hợp này trong tất cả các printftriển khai.


2

Vấn đề về echo và printf chỉ liên quan đến sự hiểu biết khi một ký tự được trích dẫn trở lại là một "ký tự đặc biệt".

Đơn giản nhất là với một chuỗi trong printf '%s' "$string".
Trong trường hợp này, không có ký tự đặc biệt nào để xử lý và mọi thứ mà lệnh printf nhận được trong đối số thứ hai được in nguyên trạng.

Lưu ý rằng chỉ có dấu ngoặc đơn được sử dụng:

$ printf '%s\n' '\\\\\\\\\T '       # nine \
\\\\\\\\\T                          # nine \

Khi chuỗi được sử dụng làm đối số đầu tiên, một số ký tự là đặc biệt.
Một \\cặp đại diện cho một đơn \\Tmột đơn T:

$ printf '\\\\\\\\\T '              # nine \
\\\\T                               # four \

Mỗi cặp trong số bốn cặp \\biến đổi thành một \và cuối cùng \Tthành a T.

$ printf '\\\\\\\\\a '              # nine \
\\\\                                # four \

Mỗi trong số bốn cặp \\biến đổi thành một ký tự duy nhất \và cuối cùng \athành một ký tự chuông (BEL) (không in được).

Điều tương tự xảy ra với một số thực hiện tiếng vang.

Việc thực hiện dấu gạch ngang luôn biến đổi các ký tự dấu gạch chéo ngược đặc biệt.

Nếu chúng ta đặt mã này trong một tập lệnh:

set -- '\g ' '\\g ' '\\\g ' '\\\\g ' '\\\\\g ' '\\\\\\g ' '\\\\\\\g ' '\\\\\\\\g ' '\\\\\\\\\g '
for i ; do
    printf '<%-14s> \t<%-9s> \t<%-14s> \t<%-12s>\n' \
       "$(printf '%s ' "|$i|")" \
       "$(printf       "|$i|")" \
           "$(echo         "|$i|")" \
       "$(echo    -e   "|$i|")" ;
done

Sau đó, dấu gạch ngang sẽ in ( dash ./script):

<|\g |         >        <|\g |    >     <|\g |         >        <-e |\g |    >
<|\\g |        >        <|\g |    >     <|\g |         >        <-e |\g |    >
<|\\\g |       >        <|\\g |   >     <|\\g |        >        <-e |\\g |   >
<|\\\\g |      >        <|\\g |   >     <|\\g |        >        <-e |\\g |   >
<|\\\\\g |     >        <|\\\g |  >     <|\\\g |       >        <-e |\\\g |  >
<|\\\\\\g |    >        <|\\\g |  >     <|\\\g |       >        <-e |\\\g |  >
<|\\\\\\\g |   >        <|\\\\g | >     <|\\\\g |      >        <-e |\\\\g | >
<|\\\\\\\\g |  >        <|\\\\g | >     <|\\\\g |      >        <-e |\\\\g | >
<|\\\\\\\\\g | >        <|\\\\\g |>     <|\\\\\g |     >        <-e |\\\\\g |>

Hai cột đầu tiên sẽ giống nhau (printf) cho tất cả các shell.
Hai cái còn lại sẽ thay đổi với việc thực hiện cụ thể của tiếng vang được sử dụng.

Ví dụ: ash ./script(busybox ash):

<|\g |         >        <|\g |    >     <|\g |         >        <|\g |       >
<|\\g |        >        <|\g |    >     <|\\g |        >        <|\g |       >
<|\\\g |       >        <|\\g |   >     <|\\\g |       >        <|\\g |      >
<|\\\\g |      >        <|\\g |   >     <|\\\\g |      >        <|\\g |      >
<|\\\\\g |     >        <|\\\g |  >     <|\\\\\g |     >        <|\\\g |     >
<|\\\\\\g |    >        <|\\\g |  >     <|\\\\\\g |    >        <|\\\g |     >
<|\\\\\\\g |   >        <|\\\\g | >     <|\\\\\\\g |   >        <|\\\\g |    >
<|\\\\\\\\g |  >        <|\\\\g | >     <|\\\\\\\\g |  >        <|\\\\g |    >
<|\\\\\\\\\g | >        <|\\\\\g |>     <|\\\\\\\\\g | >        <|\\\\\g |   >

Nếu ký tự được sử dụng là một a, cho dấu gạch ngang:

<|\a |         >        <| |     >      <| |          >         <-e | |     >
<|\\a |        >        <|\a |    >     <|\a |         >        <-e |\a |    >
<|\\\a |       >        <|\ |    >      <|\ |         >         <-e |\ |    >
<|\\\\a |      >        <|\\a |   >     <|\\a |        >        <-e |\\a |   >
<|\\\\\a |     >        <|\\ |   >      <|\\ |        >         <-e |\\ |   >
<|\\\\\\a |    >        <|\\\a |  >     <|\\\a |       >        <-e |\\\a |  >
<|\\\\\\\a |   >        <|\\\ |  >      <|\\\ |       >         <-e |\\\ |  >
<|\\\\\\\\a |  >        <|\\\\a | >     <|\\\\a |      >        <-e |\\\\a | >
<|\\\\\\\\\a | >        <|\\\\ | >      <|\\\\ |      >         <-e |\\\\ | >

Và cho bash:

<|\a |         >        <| |     >      <|\a |         >        <| |        >
<|\\a |        >        <|\a |    >     <|\\a |        >        <|\a |       >
<|\\\a |       >        <|\ |    >      <|\\\a |       >        <|\ |       >
<|\\\\a |      >        <|\\a |   >     <|\\\\a |      >        <|\\a |      >
<|\\\\\a |     >        <|\\ |   >      <|\\\\\a |     >        <|\\ |      >
<|\\\\\\a |    >        <|\\\a |  >     <|\\\\\\a |    >        <|\\\a |     >
<|\\\\\\\a |   >        <|\\\ |  >      <|\\\\\\\a |   >        <|\\\ |     >
<|\\\\\\\\a |  >        <|\\\\a | >     <|\\\\\\\\a |  >        <|\\\\a |    >
<|\\\\\\\\\a | >        <|\\\\ | >      <|\\\\\\\\\a | >        <|\\\\ |    >

Do đó, chúng ta phải thêm phần giải thích rằng shell là các lệnh đang được thực thi cũng có thể áp dụng cho chuỗi ký tự.

$ printf '%s\n' '\\\\T '
\\\\T
$ printf '%s\n' "\\\\T "
\\T

Lưu ý rằng shell thực hiện một số hành động đối với dấu gạch chéo ngược bên trong dấu ngoặc kép.

Với mã này:

tab='   '
say(){ echo "$(printf '%s' "$a") $tab $(echo "$a") $tab $(echo -e "$a")"; }
a="one \a "         ; say
a="two \\a "        ; say
a="t33 \\\a "       ; say
a="f44 \\\\a "      ; say
a="f55 \\\\\a "     ; say
a="s66 \\\\\\a "    ; say
a="s77 \\\\\\\a "   ; say
a="e88 \\\\\\\\a "  ; say
a="n99 \\\\\\\\\a " ; say

Cả hai hiệu ứng đều được thêm vào và chúng tôi nhận được điều này:

$ bash ./script
one \a           one \a          one  
two \a           two \a          two  
t33 \\a          t33 \\a         t33 \a 
f44 \\a          f44 \\a         f44 \a 
f55 \\\a         f55 \\\a        f55 \ 
s66 \\\a         s66 \\\a        s66 \ 
s77 \\\\a        s77 \\\\a       s77 \\a 
e88 \\\\a        e88 \\\\a       e88 \\a 
n99 \\\\\a       n99 \\\\\a      n99 \\ 

Đối với dấu gạch ngang, nó thậm chí còn nghiêm trọng hơn:

$ dash ./script
one              one             -e one  
two              two             -e two  
t33 \a           t33             -e t33  
f44 \a           f44             -e f44  
f55 \            f55 \           -e f55 \ 
s66 \            s66 \           -e s66 \ 
s77 \\a          s77 \a          -e s77 \a 
e88 \\a          e88 \a          -e e88 \a 
n99 \\           n99 \           -e n99 \ 
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.