Câu trả lời của chính eplawless giải quyết một cách đơn giản và hiệu quả vấn đề cụ thể của anh ấy: nó thay thế tất cả các "
trường hợp trong toàn bộ danh sách đối số bằng \"
, đó là cách Bash yêu cầu dấu ngoặc kép bên trong chuỗi dấu ngoặc kép được biểu diễn.
Để trả lời chung cho câu hỏi về cách thoát dấu ngoặc kép bên trong chuỗi được trích dẫn kép bằng cách sử dụngcmd.exe
, trình thông dịch dòng lệnh của Windows (cho dù trên dòng lệnh - thường vẫn được gọi nhầm là "dấu nhắc DOS" - hoặc trong một tệp loạt): Xem phần dưới để xem PowerShell .
tl; dr :
Bạn phải sử dụng""
khi truyền một chuỗi vào tệp lô (nother) và bạn có thể sử dụng ""
với các ứng dụng được tạo bằng trình biên dịch C / C ++ /. NET của Microsoft ( cũng chấp nhận \"
), trên Windows bao gồm Python và Node.js :
\"
là bắt buộc - là tùy chọn duy nhất - bởi nhiều chương trình khác , (ví dụ: Ruby, Perl, và thậm chí cả Windows PowerShell của Microsoft (!)), nhưng VIỆC SỬ DỤNG CỦA NÓ KHÔNG AN TOÀN :
\"
là thứ mà nhiều tệp thực thi và trình thông dịch yêu cầu - bao gồm cả Windows PowerShell - khi được truyền các chuỗi từ bên ngoài - hoặc, trong trường hợp trình biên dịch của Microsoft, hỗ trợ như một giải pháp thay thế ""
- tuy nhiên, cuối cùng, việc phân tích cú pháp danh sách đối số tùy thuộc vào chương trình mục tiêu .
- Thí dụ:
foo.exe "We had 3\" of rain."
- TUY NHIÊN, VIỆC SỬ DỤNG
\"
CÓ THỂ ĐƯA RA TRONG VIỆC THI CÔNG BẮT BUỘC, KHÔNG MONG MUỐN VÀ / hoặc CHỈNH SỬA ĐẦU VÀO / ĐẦU RA :
- Các ký tự sau đây có nguy cơ này:
& | < >
- Ví dụ, các kết quả sau đây dẫn đến việc thực thi lệnh không theo ý muốn
ver
; xem thêm bên dưới để có lời giải thích và gạch đầu dòng tiếp theo để biết cách giải quyết:
foo.exe "3\" of snow" "& ver."
- Đối với Windows PowerShell ,
\""
và "^""
là mạnh mẽ, nhưng lựa chọn thay thế hạn chế (xem phần "Calling CLI PowerShell của ..." dưới đây).
Nếu bạn buộc phải sử dụng \"
, chỉ có 3 cách tiếp cận an toàn , tuy nhiên khá rườm rà : Nộp mũ cho TS để được giúp đỡ.
Sử dụng mở rộng biến bị trì hoãn (có thể có chọn lọc ) trong tệp loạt của bạn, bạn có thể lưu trữ nghĩa đen \"
trong một biến và tham chiếu biến đó bên trong một "..."
chuỗi bằng !var!
cú pháp - hãy xem câu trả lời hữu ích của TS .
- Cách tiếp cận trên, mặc dù rườm rà, nhưng có ưu điểm là bạn có thể áp dụng nó một cách bài bản và nó hoạt động mạnh mẽ , với bất kỳ đầu vào nào.
Chỉ với các chuỗi LITERAL - những chuỗi KHÔNG liên quan đến BIẾN - bạn mới có được cách tiếp cận có phương pháp tương tự: phân loại ^
-escape tất cả các cmd.exe
siêu ký tự: " & | < >
và - nếu bạn cũng muốn ngăn chặn mở rộng biến - %
:
foo.exe ^"3\^" of snow^" ^"^& ver.^"
Nếu không, bạn phải xây dựng chuỗi của mình dựa trên việc nhận ra những phần nào của chuỗi cmd.exe
được coi là không được trích dẫn do hiểu sai\"
là dấu phân cách đóng:
trong các phần chữ có chứa siêu ký tự shell: ^
-escape chúng; sử dụng ví dụ trên, nó &
phải được ^
-escaped:
foo.exe "3\" of snow" "^& ver."
trong các phần có %...%
tham chiếu biến -style : đảm bảo cmd.exe
coi chúng là một phần của "..."
chuỗi và bản thân các giá trị biến không có dấu ngoặc kép, không cân bằng được nhúng - điều này thậm chí không phải lúc nào cũng có thể thực hiện được .
Để biết thông tin cơ bản, hãy đọc tiếp.
Lý lịch
Lưu ý: Điều này dựa trên các thử nghiệm của riêng tôi. Hãy cho tôi biết nếu tôi sai.
Các shell giống POSIX chẳng hạn như Bash trên các hệ thống giống Unix mã hóa danh sách đối số (chuỗi) trước khi chuyển các đối số riêng lẻ đến chương trình đích: trong số các mở rộng khác, chúng chia danh sách đối số thành các từ riêng lẻ (tách từ) và xóa các ký tự trích dẫn khỏi từ kết quả (loại bỏ trích dẫn). Chương trình đích được đưa ra một loạt các đối số riêng lẻ , với các dấu ngoặc kép bị loại bỏ .
Ngược lại, trình thông dịch lệnh của Windows dường như không mã hóa danh sách đối số và chỉ chuyển một chuỗi đơn bao gồm tất cả các đối số - bao gồm cả các ký tự trích dẫn. - đối với chương trình mục tiêu.
Tuy nhiên, một số quá trình xử lý trước diễn ra trước khi chuỗi đơn được chuyển đến chương trình đích: ^
ký tự thoát. bên ngoài các chuỗi được trích dẫn kép bị loại bỏ (chúng thoát khỏi ký tự sau), và các tham chiếu biến (ví dụ %USERNAME%
:) được nội suy trước.
Do đó, không giống như trong Unix, chương trình đích có trách nhiệm phân tích cú pháp để phân tích chuỗi đối số và chia nhỏ nó thành các đối số riêng lẻ với dấu ngoặc kép bị loại bỏ. Do đó, theo giả thuyết , các chương trình khác nhau có thể yêu cầu các phương pháp thoát khác nhau và không có cơ chế thoát duy nhất nào được đảm bảo hoạt động với tất cả các chương trình - https://stackoverflow.com/a/4094897/45375 chứa thông tin cơ bản tuyệt vời về chế độ vô chính phủ là dòng lệnh Windows phân tích cú pháp.
Trong thực tế, \"
rất phổ biến, nhưng KHÔNG AN TOÀN , như đã đề cập ở trên:
Vì cmd.exe
bản thân nó không nhận ra \"
là một dấu ngoặc kép thoát , nó có thể cấu trúc sai các mã thông báo sau này trên dòng lệnh là không được trích dẫn và có khả năng diễn giải chúng thành lệnh và / hoặc chuyển hướng đầu vào / đầu ra .
Tóm lại: vấn đề bề mặt, nếu bất kỳ của các nhân vật sau đây theo một mở hoặc không cân bằng \"
:& | < >
; ví dụ:
foo.exe "3\" of snow" "& ver."
cmd.exe
thấy các mã thông báo sau, do hiểu sai \"
thành một dấu ngoặc kép thông thường:
"3\"
of
snow" "
- nghỉ ngơi:
& ver.
Vì cmd.exe
nghĩ rằng nó không & ver.
được trích dẫn , nên nó diễn giải nó là &
(toán tử sắp xếp lệnh), theo sau là tên của lệnh để thực thi ( ver.
- lệnh .
bị bỏ qua; thông tin phiên bản của ver
báo cáo cmd.exe
).
Hiệu quả tổng thể là:
- Đầu tiên, chỉ
foo.exe
được gọi với 3 mã thông báo đầu tiên .
- Sau đó, lệnh
ver
được thực hiện.
Ngay cả trong trường hợp lệnh ngẫu nhiên không gây hại, lệnh tổng thể của bạn sẽ không hoạt động như thiết kế, vì không phải tất cả các đối số đều được chuyển cho nó.
Nhiều trình biên dịch / thông dịch viên CHỈ nhận ra\"
- ví dụ: trình biên dịch GNU C / C ++, Python, Perl, Ruby, thậm chí cả Windows PowerShell của Microsoft khi được gọi từ cmd.exe
- và, ngoại trừ (có giới hạn) đối với Windows PowerShell \""
, đối với họ , không có giải pháp đơn giản nào vấn đề này.
Về cơ bản, bạn phải biết trước phần nào trong dòng lệnh của bạn bị hiểu sai là chưa được trích dẫn và có chọn lọc^
tất cả các phiên bản của & | < >
những phần đó.
Ngược lại, việc sử dụng ""
AN TOÀN , nhưng đáng tiếc là chỉ được hỗ trợ bởi các tệp thực thi dựa trên trình biên dịch của Microsoft và các tệp hàng loạt (trong trường hợp tệp hàng loạt, với các câu hỏi được thảo luận ở trên), đáng chú ý là loại trừ PowerShell - xem phần tiếp theo.
Gọi CLI của PowerShell từ cmd.exe
hoặc các shell giống như POSIX:
Lưu ý: Xem phần dưới cùng để biết cách xử lý trích dẫn bên trong PowerShell.
Khi được gọi từ bên ngoài - ví dụ: từ cmd.exe
, cho dù từ dòng lệnh hay tệp lô:
PowerShell [Core] v6 + hiện đã nhận dạng đúng""
(ngoài ra\"
), vừa an toàn để sử dụng vừa bảo vệ khoảng trắng .
pwsh -c " ""a & c"".length "
không bị vỡ và cho ra một cách chính xác 6
Windows PowerShell (phiên bản kế thừa có phiên bản cuối cùng là 5.1) chỉ nhận dạng và trên Windows cũng có và mạnh mẽ hơn / \"
"""
\""
"^""
(mặc dùPowerShell nội bộ sử dụng`
làm ký tự thoát trong các chuỗi được trích dẫn kép và cũng chấp nhận""
- xem phần dưới cùng):
Gọi Windows PowerShell từcmd.exe
/ một tệp hàng loạt:
""
ngắt , bởi vì về cơ bản nó không được hỗ trợ:
powershell -c " ""ab c"".length "
-> lỗi "Chuỗi bị thiếu dấu chấm"
\"
và """
hoạt động theo nguyên tắc , nhưng không an toàn :
powershell -c " \"ab c\".length "
hoạt động như dự định: nó xuất ra 5
(lưu ý 2 dấu cách)
- Nhưng nó không an toàn, bởi vì
cmd.exe
siêu ký tự phá vỡ lệnh, trừ khi thoát:
powershell -c " \"a& c\".length "
break , do &
, sẽ phải được thoát như^&
\""
là an toàn , nhưng bình thường hóa khoảng trắng bên trong , điều này có thể không mong muốn:
powershell -c " \""a& c\"".length "
đầu ra 4
(!), vì 2 khoảng trắng được chuẩn hóa thành 1.
"^""
là lựa chọn tốt nhất cho Windows PowerShell cụ thể , nơi nó vừa an toàn vừa bảo toàn khoảng trắng, nhưng với PowerShell Core (trên Windows), nó cũng giống như\""
, tức là chuẩn hóa khoảng trắng . Tín dụng dành cho Venryx vì đã khám phá ra phương pháp này.
powershell -c " "^""a& c"^"".length "
hoạt động : không phá vỡ - mặc dù &
- và đầu ra 5
, tức là khoảng trắng được bảo toàn chính xác.
PowerShell Core : pwsh -c " "^""a& c"^"".length "
hoạt động , nhưng đầu ra 4
, tức là chuẩn hóa khoảng trắng , cũng như \""
vậy.
Trên các nền tảng giống Unix (Linux, macOS), khi gọi CLI của PowerShell [Core]pwsh
, từ một trình bao giống POSIX chẳng hạn như bash
:
Bạn phải sử dụng\"
, tuy nhiên, cả hai đều an toàn và bảo vệ khoảng trắng :
$ pwsh -c " \"a& c|\".length" # OK: 5
Thông tin liên quan
^
chỉ có thể được sử dụng làm ký tự thoát trong các chuỗi không được trích dẫn - bên trong các chuỗi được trích dẫn kép, ^
không đặc biệt và được coi là một ký tự.
- CAVEAT : Việc sử dụng
^
trong các tham số được truyền cho call
câu lệnh bị hỏng (điều này áp dụng cho cả hai cách sử dụng call
: gọi tệp lô hoặc tệp nhị phân khác và gọi một chương trình con trong cùng tệp lô):
^
các trường hợp trong giá trị được đặt trong dấu ngoặc kép được nhân đôi một cách khó hiểu , làm thay đổi giá trị đang được truyền: ví dụ: nếu biến %v%
chứa giá trị chữ a^b
, hãy call :foo "%v%"
gán "a^^b"
(!) cho %1
(tham số đầu tiên) trong chương trình con :foo
.
- Không thể viện chứng sử dụng
^
với call
được phá vỡ hoàn toàn trong đó ^
có thể không còn được sử dụng để thoát khỏi nhân vật đặc biệt : ví dụ,call foo.cmd a^&b
lặng lẽ phá vỡ (thay vì đi qua đena&b
quáfoo.cmd
, như sẽ là trường hợp mà không cầncall
) -foo.cmd
không bao giờ thậm chí gọi, ít nhất là trên Windows (!) 7.
%
Thật không may, thoát khỏi một ký tự là một trường hợp đặc biệt , đòi hỏi cú pháp riêng biệt tùy thuộc vào việc một chuỗi được chỉ định trên dòng lệnh so với bên trong một tệp loạt ; xem https://stackoverflow.com/a/31420292/45375
- Tóm tắt của nó: Bên trong một tệp hàng loạt, sử dụng
%%
. Trên dòng lệnh, %
không thể thoát được, nhưng nếu bạn đặt một ^
ở đầu, cuối hoặc bên trong tên biến trong một chuỗi không được trích dẫn (ví dụ echo %^foo%
:), bạn có thể ngăn việc mở rộng biến (nội suy); %
các trường hợp trên dòng lệnh không phải là một phần của tham chiếu biến được coi là các ký tự (ví dụ 100%
:).
Nói chung, để làm việc an toàn với các giá trị biến có thể chứa khoảng trắng và ký tự đặc biệt :
- Chuyển nhượng : kèm theo cả tên biến và giá trị trong một đơn cặp dấu nháy kép ; ví dụ:
set "v=a & b"
gán giá trị theo nghĩa đen a & b
cho biến %v%
(ngược lại, set v="a & b"
sẽ làm cho phần trong dấu ngoặc kép của giá trị). Thoát các phiên bản theo nghĩa đen %
dưới dạng %%
(chỉ hoạt động trong các tệp hàng loạt - xem ở trên).
- Tham chiếu : Các tham chiếu biến dấu ngoặc kép để đảm bảo giá trị của chúng không bị nội suy; ví dụ,
echo "%v%"
không phụ thuộc vào giá trị của %v%
phép nội suy và bản in "a & b"
(nhưng lưu ý rằng dấu ngoặc kép luôn luôn được in). Ngược lại, echo %v%
chuyển theo nghĩa đen a
tới echo
, được hiểu &
là toán tử sắp xếp lệnh, và do đó cố gắng thực thi một lệnh được đặt tên b
.
Cũng lưu ý việc sử dụng lại báo trước ở trên ^
với call
câu lệnh.
- Các chương trình bên ngoài thường quan tâm đến việc loại bỏ dấu ngoặc kép bao quanh các tham số, nhưng, như đã lưu ý, trong các tệp hàng loạt, bạn phải tự thực hiện việc đó (ví dụ:
%~1
để loại bỏ dấu ngoặc kép bao quanh tham số đầu tiên) và, thật đáng tiếc, không có trực tiếp cách mà tôi biết echo
để in một giá trị biến một cách trung thực mà không có dấu ngoặc kép đi kèm .
- Neil đưa ra
for
giải pháp dựa trên cơ sở hoạt động miễn là giá trị không có dấu ngoặc kép được nhúng ; ví dụ:
set "var=^&')|;,%!"
for /f "delims=" %%v in ("%var%") do echo %%~v
cmd.exe
không không nhận đơn -quotes như delimiters chuỗi - họ được đối xử như literals và không thể thường được sử dụng để phân định chuỗi với khoảng trắng nhúng; Ngoài ra, nó theo sau rằng các mã thông báo tiếp giáp với các dấu ngoặc đơn và bất kỳ mã thông báo nào ở giữa được coi là không được trích dẫn cmd.exe
và diễn giải theo đó.
- Tuy nhiên, do các chương trình đích cuối cùng thực hiện phân tích cú pháp đối số của riêng chúng, một số chương trình như Ruby không nhận ra các chuỗi được trích dẫn đơn ngay cả trên Windows; ngược lại, các tệp thực thi C / C ++, Perl và Python không nhận ra chúng.
Tuy nhiên, ngay cả khi được hỗ trợ bởi chương trình mục tiêu, không nên sử dụng các chuỗi được trích dẫn đơn, vì nội dung của chúng không được bảo vệ khỏi sự diễn giải không mong muốn tiềm ẩn bởi cmd.exe
.
Trích dẫn từ bên trong PowerShell:
Windows PowerShell là một shell tiên tiến hơn nhiều cmd.exe
và nó đã là một phần của Windows trong nhiều năm nay (và PowerShell Core cũng mang trải nghiệm PowerShell cho macOS và Linux).
PowerShell hoạt động nội bộ nhất quán liên quan đến việc trích dẫn:
- bên trong các chuỗi dấu ngoặc kép , sử dụng
`"
hoặc ""
để thoát khỏi dấu ngoặc kép
- bên trong các chuỗi được trích dẫn đơn, sử dụng
''
để thoát khỏi các dấu ngoặc đơn
Điều này hoạt động trên dòng lệnh PowerShell và khi chuyển các tham số tới các tập lệnh hoặc chức năng PowerShell từ bên trong PowerShell.
(Như đã thảo luận ở trên, việc chuyển một dấu ngoặc kép thoát cho PowerShell từ bên ngoài yêu cầu \"
hoặc mạnh hơn là \""
- không có gì khác hoạt động).
Đáng buồn thay, khi gọi các chương trình bên ngoài từ PowerShell, bạn phải đối mặt với nhu cầu vừa đáp ứng các quy tắc trích dẫn riêng của PowerShell vừa thoát khỏi chương trình đích :
Hành vi có vấn đề này cũng được thảo luận và tóm tắt trong câu trả lời này
Đôi -quotes bên trong đôi dây -quoted :
Hãy xem xét chuỗi "3`" of rain"
, mà PowerShell-nội bộ dịch sang nghĩa đen3" of rain
.
Nếu bạn muốn chuyển chuỗi này tới một chương trình bên ngoài, bạn phải áp dụng cách thoát của chương trình đích ngoài PowerShell ; giả sử bạn muốn chuyển chuỗi đến một chương trình C, chương trình này yêu cầu dấu ngoặc kép nhúng được thoát ra dưới dạng \"
:
foo.exe "3\`" of rain"
Lưu ý cách cả hai `"
- để làm cho PowerShell hạnh phúc - và các \
- để làm cho chương trình mục tiêu hạnh phúc - phải có mặt.
Logic tương tự cũng áp dụng cho việc gọi một tệp loạt, nơi ""
phải được sử dụng:
foo.bat "3`"`" of rain"
Ngược lại, việc nhúng các dấu ngoặc kép đơn trong một chuỗi có dấu ngoặc kép không cần phải thoát.
Độc -quotes bên đơn chuỗi -quoted làm không đòi hỏi thêm thoát; hãy xem xét'2'' of snow'
, đó là đại diện của PowerShell2' of snow
.
foo.exe '2'' of snow'
foo.bat '2'' of snow'
PowerShell dịch các chuỗi được trích dẫn đơn thành các chuỗi được trích dẫn kép trước khi chuyển chúng đến chương trình đích.
Tuy nhiên, các dấu ngoặc kép bên trong các chuỗi được trích dẫn đơn , không cần thoát cho PowerShell , vẫn cần được thoát cho chương trình đích :
foo.exe '3\" of rain'
foo.bat '3"" of rain'
PowerShell v3 đã giới thiệu --%
tùy chọn kỳ diệu , được gọi là biểu tượng phân tích cú pháp dừng , giúp giảm bớt một số đau đớn, bằng cách chuyển bất kỳ thứ gì sau khi nó chưa được diễn giải cho chương trình đích, lưu cho cmd.exe
các tham chiếu biến môi trường kiểu (ví dụ %USERNAME%
:), được mở rộng; ví dụ:
foo.exe --% "3\" of rain" -u %USERNAME%
Lưu ý cách thoát được nhúng "
như chỉ \"
dành cho chương trình đích (và không phải đối với PowerShell dưới dạng \`"
) là đủ.
Tuy nhiên, cách tiếp cận này:
- không cho phép các ký tự thoát
%
để tránh mở rộng theo biến môi trường.
- ngăn chặn việc sử dụng trực tiếp các biến và biểu thức PowerShell; thay vào đó, dòng lệnh phải được tạo trong một biến chuỗi trong bước đầu tiên và sau đó được gọi với
Invoke-Expression
trong một giây.
Do đó, mặc dù có nhiều cải tiến, PowerShell đã không giúp việc thoát dễ dàng hơn khi gọi các chương trình bên ngoài. Tuy nhiên, nó đã giới thiệu hỗ trợ cho các chuỗi được trích dẫn đơn.
Tôi tự hỏi nếu nó về cơ bản có thể có trong thế giới Windows để chuyển đổi bao giờ hết để mô hình Unix để cho các vỏ làm tất cả việc loại bỏ tokenization và trích dẫn dự đoán , lên phía trước , không phụ thuộc vào chương trình mục tiêu , và sau đó gọi chương trình mục tiêu bằng cách thông qua các thẻ kết quả .