Власна відповідь eplawless просто та ефективно вирішує його конкретну проблему: вона замінює всі "
екземпляри у всьому списку аргументів на \"
, саме таким чином Bash вимагає представлення подвійних лапок всередині рядка з подвійними лапками.
Щоб загалом відповісти на питання про те, як уникнути подвійних лапок всередині рядка з подвійними лапками, використовуючиcmd.exe
інтерпретатор командного рядка Windows (будь то в командному рядку - часто все ще помилково називається "підказкою DOS" - або в командному файлі): Дивіться знизу, щоб подивитися PowerShell .
tl; dr :
Ви повинні використовувати""
при передачі рядка ((нічого) пакетного файлу, і ви можете використовувати ""
з програмами, створеними за допомогою компіляторів C / C ++ /. NET від Microsoft (які також приймають \"
), які в Windows включають Python та Node.js :
\"
це потрібно - як єдиний варіант - багатьма іншими програмами , (! наприклад, Ruby, Perl, і навіть Microsoft власний Windows PowerShell ()), але ЙОГО ВИКОРИСТАННЯ НЕ SAFE :
\"
це те, що вимагається багатьом виконуваним файлам і інтерпретаторам - включаючи Windows PowerShell - при передачі рядків ззовні - або, у випадку компіляторів Microsoft, підтримка як альтернатива ""
- в кінцевому рахунку, цільова програма повинна проаналізувати список аргументів .
- Приклад:
foo.exe "We had 3\" of rain."
- ОДНО, ВИКОРИСТАННЯ
\"
МОЖЕ РЕЗУЛЬТАТУВАТИ НЕБАЖЛИВЕ, АРТІЦІЙНЕ ВИКОНАННЯ КОМАНД і / або ВХОДИ / ВИХОДНІ НАПРЯМКИ :
- Наступні символи представляють цей ризик:
& | < >
- Наприклад, наступне призводить до ненавмисного виконання
ver
команди; див. далі пояснення та наступний пункт для обхідного шляху:
foo.exe "3\" of snow" "& ver."
- Для Windows PowerShell ,
\""
і "^""
надійні, але обмежені варіанти (дивись розділ «Виклик CLI PowerShell в ...» нижче).
Якщо вам потрібно скористатися \"
, існує лише 3 безпечні підходи , які, проте, досить громіздкі : Підказка капелюха ТС за його допомогою.
Використовуючи (можливо, вибіркове ) затримку розширення змінної у вашому пакетному файлі, ви можете зберігати літерал \"
у змінній та посилатися на цю змінну всередині "..."
рядка, використовуючи !var!
синтаксис - див . Корисну відповідь TS .
- Вищезазначений підхід, незважаючи на громіздкість, має ту перевагу, що ви можете застосовувати його методично і що він працює надійно , з будь-яким входом.
Лише з ЛІТЕРАЛЬНИМИ рядками - такими, що НЕ включають ЗМІННИХ - ви отримуєте аналогічний методичний підхід: категорично ^
-бежать всі cmd.exe
метасимволи: " & | < >
і - якщо ви також хочете придушити змінне розширення - %
:
foo.exe ^"3\^" of snow^" ^"^& ver.^"
В іншому випадку, ви повинні сформулювати свій рядок на основі розпізнавання, які частини рядка cmd.exe
вважають котируваннями через неправильне тлумачення\"
як закриваючі роздільники:
в буквальних частинах , що містять метасимволи: ^
екранують їх; використовуючи приклад, наведений вище, це &
те, що має бути ^
-escaped:
foo.exe "3\" of snow" "^& ver."
частинами з %...%
посиланнями на змінні -style : переконайтеся, що вони cmd.exe
вважають їх частиною "..."
рядка і що значення змінних самі не мають вбудованих незбалансованих лапок - що навіть не завжди можливо .
Для довідкової інформації читайте далі.
Передумови
Примітка: Це базується на моїх власних експериментах. Повідомте мене, якщо я помиляюся.
POSIX-подібні оболонки, такі як Bash на Unix-подібних системах, токенізують список аргументів (рядок) перед передачею аргументів окремо цільовій програмі: серед інших розширень вони розділяють список аргументів на окремі слова (розбиття слів) і видаляють символи цитування з отримані слова (вилучення цитати). Цільовій програмі вручається масив з окремих аргументів , з синтаксичними цитати видалені .
На відміну від цього, інтерпретатор команд Windows, очевидно, не маркує список аргументів, а просто передає єдиний рядок, що включає всі аргументи, включаючи символи цитування. - до цільової програми.
Однак відбувається певна попередня обробка перед передачею одного рядка цільовій програмі: ^
escape символи. поза рядків із подвійними лапками видаляються (вони уникають наступного символу), а посилання на змінні (наприклад, %USERNAME%
) спочатку інтерполюються .
Таким чином, на відміну від Unix, цільова програма несе відповідальність за синтаксичний аналіз, щоб проаналізувати рядок аргументів і розбити його на окремі аргументи із видаленими лапками. Таким чином, різні програми можуть гіпотетично вимагати різних методів екранування, і немає єдиного механізму екранування, який гарантовано буде працювати з усіма програмами - https://stackoverflow.com/a/4094897/45375 містить чудові передумови про анархію, тобто командний рядок Windows синтаксичний розбір.
На практиці \"
це дуже поширене, але НЕ БЕЗПЕЧНО , як уже згадувалося вище:
Так як cmd.exe
сам по собі не визнає \"
як бігла прямі подвійні лапки, він може неправильно витлумачити пізніше лексеми в командному рядку, без лапок і потенційно інтерпретувати їх як команду і / або введення / виведення перенаправлення .
У двох словах: проблемна поверхня, якщо будь-який із наведених нижче символів слідує за відкриттям або незбалансованим \"
:& | < >
; наприклад:
foo.exe "3\" of snow" "& ver."
cmd.exe
бачить наступні лексеми, отримані в результаті неправильного тлумачення \"
як звичайні подвійні лапки:
"3\"
of
snow" "
- відпочинок:
& ver.
Оскільки cmd.exe
вважає, що & ver.
це не котирування , воно інтерпретує це як &
(оператор послідовності команд), а потім ім'я команди для виконання ( ver.
- .
ігнорується;ver
про cmd.exe
версію звітів ).
Загальний ефект:
- Перший,
foo.exe
викликається лише першими 3 маркерами.
- Потім, команда
ver
виконується.
Навіть у тих випадках, коли випадкова команда не завдає шкоди, ваша загальна команда не працюватиме так, як було розроблено, враховуючи те, що не всі аргументи передаються їй.
Багато компілятори / інтерпретатори розпізнають ТІЛЬКИ\"
- наприклад, компілятор GNU C / C ++, Python, Perl, Ruby, навіть власна оболонка Windows PowerShell від Microsoft при виклику cmd.exe
- і, за винятком (з обмеженнями) для Windows PowerShell with \""
, для них немає простого рішення до цієї проблеми.
По суті, вам слід було б заздалегідь знати, які частини вашого командного рядка неправильно трактуються як нецитовані та вибірково^
нецитовані уникати всіх екземплярів & | < >
цих частин.
Навпаки, використання ""
є БЕЗПЕЧНО , але, на жаль, воно підтримується лише виконуваними файлами та пакетними файлами на базі компілятора Microsoft (у випадку з пакетними файлами, з примхами, про які йшлося вище), що особливо виключає PowerShell - див. Наступний розділ.
Виклик CLI PowerShell з cmd.exe
оболонок, схожих на POSIX:
Примітка: Дивіться нижній розділ, щоб дізнатись, як обробляється цитування в PowerShell.
При виклику ззовні - наприклад, з cmd.exe
командного рядка чи командного файлу:
PowerShell [Core] v6 + тепер правильно розпізнає""
(крім\"
), що є безпечним у використанні та зберігає пробіли .
pwsh -c " ""a & c"".length "
не ламається і правильно дає 6
Windows PowerShell (застаріле видання, остання версія якого 5.1) розпізнає лише, \"
а в Windows також """
і більш надійний \""
/"^""
(навіть якщо внутрішньо PowerShell використовує`
як вхідний символ у рядках із подвійними лапками, а також приймає""
- див. Нижній розділ):
Виклик Windows PowerShell зcmd.exe
/ командного файлу:
""
перерви , оскільки це принципово не підтримується:
powershell -c " ""ab c"".length "
-> помилка "У рядку відсутній термінатор"
\"
і """
працюють в принципі , але не в безпеці :
powershell -c " \"ab c\".length "
працює за призначенням: видає 5
(зверніть увагу на 2 пробіли)
- Але це не безпечно, тому що
cmd.exe
метасимволи порушують команду, якщо не захищено :
powershell -c " \"a& c\".length "
перерви , через те &
, що потрібно було б уникнути як^&
\""
є безпечним , але нормалізують інтер'єр прогалини , які можуть бути небажаними:
powershell -c " \""a& c\"".length "
виводить 4
(!), оскільки 2 пробіли нормуються до 1.
"^""
це кращий вибір для Windows PowerShell конкретно , де вона є безпечною і пробільним зберігає, але з PowerShell Ядром (на Windows) , це те ж саме , як\""
, наприклад, whitespace- нормалізації . За відкриття цього підходу Venryx заслуговує на честь .
powershell -c " "^""a& c"^"".length "
працює : не ламається - попри &
- і виводить 5
, тобто правильно збережений пробіл.
Ядро PowerShell : pwsh -c " "^""a& c"^"".length "
працює , але видає 4
, тобто нормалізує пробіли , як \""
це робить.
На Unix-подібних платформах (Linux, macOS) під час виклику CLI PowerShell [Core]pwsh
з POSIX-подібної оболонки, наприкладbash
:
Ви повинні використовувати\"
, що, однак, є і безпечним, і зберігаючи пробіли :
$ pwsh -c " \"a& c|\".length" # OK: 5
Пов’язана інформація
^
може використовуватися лише як символ втечі в рядках без лапок - всередині рядків із подвійними лапками, ^
не є особливим і трактується як літерал.
- CAVEAT : Використання
^
параметрів, переданих call
оператору, порушено (це стосується як використання call
: виклику іншого пакетного файлу або двійкового файлу, так і виклику підпрограми в тому ж пакетному файлі):
^
екземпляри в подвійних лапках значень незрозуміло подвоюються , змінюючи передане значення: наприклад, якщо змінна %v%
містить літеральне значення a^b
, call :foo "%v%"
присвоює "a^^b"
(!) %1
першому параметру в підпрограмі :foo
.
- Некотируваних використання
^
з call
буде повністю непрацездатним в тому , що ^
не може більше використовуватися , щоб екранувати спеціальні символи : наприклад,call foo.cmd a^&b
спокійно перерви (замість проходження буквальнимa&b
тежfoo.cmd
, як було б у випадку безcall
) -foo.cmd
ніколи навітьвикликається, по крайней мерена ОС Windows (!) 7.
На%
жаль, уникнення літералу - це особливий випадок , який вимагає чіткого синтаксису, залежно від того, вказаний рядок у командному рядку проти всередині командного файлу ; див. https://stackoverflow.com/a/31420292/45375
- Короткий зміст: використовуйте всередині командного файлу
%%
. У командному рядку %
неможливо уникнути екранування, але якщо розмістити a ^
на початку, в кінці або всередині імені змінної у рядку без котирувань (наприклад, echo %^foo%
), ви можете запобігти розширенню змінної (інтерполяція); %
екземпляри в командному рядку, які не є частиною посилання на змінну, розглядаються як літерали (наприклад, 100%
).
Як правило, для безпечної роботи зі значеннями змінних, які можуть містити пробіли та спеціальні символи :
- Призначення : включіть і ім’я змінної, і значення в одну пару подвійних лапок ; наприклад,
set "v=a & b"
присвоює значення a & b
змінної буквальному значенню %v%
(навпаки, set v="a & b"
це зробить подвійні лапки частиною значення). Уникайте буквальних %
примірників як %%
(працює лише в пакетних файлах - див. Вище).
- Посилання : Посилання на змінні з подвійними лапками, щоб переконатися, що їх значення не інтерпольоване; наприклад,
echo "%v%"
не підпорядковує значення %v%
інтерполяції та друку "a & b"
(але зауважте, що подвійні лапки також завжди друкуються). Навпаки, echo %v%
передає літерал a
до echo
, інтерпретує &
як оператор послідовності команд, і тому намагається виконати команду з іменем b
.
Також зверніть увагу на наведене вище застереження щодо повторного використання ^
із call
заявою.
- Зовнішні програми, як правило, піклуються про те, щоб усунути подвійні лапки навколо параметрів, але, як зазначалося, у пакетних файлах ви повинні це зробити самостійно (наприклад,
%~1
щоб видалити вкладені подвійні лапки з 1-го параметра), і, на жаль, прямого спосіб, про який я знаю, щоб echo
достовірно надрукувати значення змінної без додавання подвійних лапок .
- Ніл пропонує обхідне рішення на
for
основі, яке працює до тих пір, поки значення не має вбудованих подвійних лапок ; наприклад:
set "var=^&')|;,%!"
for /f "delims=" %%v in ("%var%") do echo %%~v
cmd.exe
зовсім НЕ визнають поодинокі -quotes , як роздільники рядків - вони розглядаються як літерали і зазвичай не можуть бути використані для визначення рядків з вкладеними пробілами; також випливає, що лексеми, що примикають до одинарних лапок, та будь-які лексеми між ними розглядаються як нецівовані cmd.exe
та інтерпретуються відповідно.
- Однак, враховуючи, що цільові програми в кінцевому підсумку виконують власний аналіз аргументів, деякі програми, такі як Ruby, розпізнають рядки з одинарними лапками навіть у Windows; навпаки, виконувані файли C / C ++, Perl та Python їх не розпізнають.
Однак навіть якщо це підтримується цільовою програмою, не доцільно використовувати рядки з одинарними лапками, оскільки їх вміст не захищений від потенційно небажаної інтерпретації cmd.exe
.
Цитуючи в PowerShell:
Windows PowerShell - набагато вдосконаленіша оболонка cmd.exe
, і вона є частиною Windows вже багато років (а PowerShell Core також привнесла досвід PowerShell у macOS та Linux).
PowerShell працює послідовно внутрішньо щодо цитування:
- всередині рядків із подвійними лапками , використовуйте
`"
або, ""
щоб уникнути подвійних лапок
- всередині рядків
''
з одинарними лапками , використовуйте для уникнення одинарних лапок
Це працює в командному рядку PowerShell і при передачі параметрів в сценарії або функції PowerShell з в PowerShell.
(Як обговорювалося вище, передача захищеної подвійної лапки до PowerShell ззовні вимагає \"
або, більш надійно,\""
- нічого іншого не працює).
На жаль, під час виклику зовнішніх програм із PowerShell ви стикаєтесь із необхідністю дотримуватись власних правил цитування PowerShell та уникати цільової програми:
Ця проблемна поведінка також обговорюється та узагальнюється у цій відповіді
Подвійні лапки всередині рядків з подвійними лапками :
Розглянемо рядок "3`" of rain"
, який PowerShell внутрішньо перекладає буквально 3" of rain
.
Якщо ви хочете передати цей рядок зовнішній програмі, вам доведеться застосувати цільову програму, що виділяється, крім PowerShell ; скажімо, що ви хочете передати рядок програмі C, яка очікує, що вбудовані подвійні лапки будуть екрановані як \"
:
foo.exe "3\`" of rain"
Зверніть увагу , як і `"
- зробити PowerShell щасливим - і\
- зробити цільову програму щасливим - повинен бути присутнім.
Та сама логіка застосовується до виклику командного файлу, де ""
повинен бути використаний:
foo.bat "3`"`" of rain"
Навпаки, вбудовування одинарних лапок у рядок із подвійними лапками взагалі не вимагає екранування .
Одиночне -quotes всередині окремих -quoted рядків нічого НЕ вимагає додаткового втечі; розглянемо'2'' of snow'
, що таке PowerShell 'представлення2' of snow
.
foo.exe '2'' of snow'
foo.bat '2'' of snow'
PowerShell переводить рядки з одним лапками у подвійні лапки, перш ніж передавати їх цільовій програмі.
Однак подвійні лапки всередині рядків з одинарними лапками , які не потребують екранування для PowerShell , все одно повинні бути екрановані для цільової програми :
foo.exe '3\" of rain'
foo.bat '3"" of rain'
PowerShell v3 представив магічний --%
варіант , званий стоп-розбір символом , який полегшує деякі болю, пропускання нічого після того, як він необроблений до цільової програми, за винятком cmd.exe
-Style середовища змінної посилання (наприклад, %USERNAME%
), які будуть розширені; наприклад:
foo.exe --% "3\" of rain" -u %USERNAME%
Зверніть увагу, як достатньо виходу із вбудованого типу "
як \"
для цільової програми (а також не для PowerShell as \`"
).
Однак такий підхід:
- не допускає екранування
%
символів, щоб уникнути розширення змінної середовища.
- виключає безпосереднє використання змінних та виразів PowerShell; натомість командний рядок повинен бути вбудований у змінну рядка на першому кроці, а потім викликаний за
Invoke-Expression
допомогою другого.
Таким чином, незважаючи на численні досягнення, PowerShell не значно полегшив втечу під час виклику зовнішніх програм. Однак він ввів підтримку рядків з одинарними лапками.
Цікаво , якщо це принципово можливо в світі Windows , щоб коли - або переключитися на модель Unix від дозволяючи оболонки зробити все лексичне і котирування видалення передбачувана , вперед , незалежно від цільової програми , а потім викликати цільову програму, передаючи отримані жетони .