Проблема с экранированием кавычек в PowerShell и plink: как избежать ошибок

Если вы передаёте SQL-запросы через PowerShell и plink, наверняка сталкивались с внезапным исчезновением кавычек в командах. Это происходит из-за особенностей обработки аргументов в PowerShell, plink и удалённой оболочке. Давайте разберёмся, как сохранить синтаксис запросов без ошибок – даже если кажется, что все варианты экранирования уже перепробованы.

Почему «съедаются» кавычки: цепочка интерпретаторов

Представьте, что ваша команда проходит через три слоя интерпретации:

  1. PowerShell (локально)
  2. Plink (клиент SSH)
  3. Удалённая оболочка (например, bash на сервере PostgreSQL)

Каждый слой пытается обработать кавычки по-своему. Например, в исходном примере:

plink -load "FIS-S-LBX-PGS-202-a" -batch echo "select ... name = 'server_version';"

PowerShell передаёт двойные кавычки в plink, но plink «не видит» их как часть аргумента для psql. В итоге удалённый сервер получает запрос без одинарных кавычек, что ломает синтаксис SQL.

Кстати, здесь работает правило: двойные кавычки обрабатываются PowerShell, а одинарные — удалённой оболочкой. Если не «защитить» их на всех уровнях, запрос превратится в name = server_version (без кавычек), и PostgreSQL выдаст ошибку.

Решение: экранирование «вглубь»

Вариант 1 – использовать обратный слэш перед двойными кавычками и экранировать одинарные:

plink -load "FIS-S-LBX-PGS-202-a" -batch psql -c --% "select setting from pg_settings where name = 'server_version';"

Здесь:

  • --% отключает парсинг PowerShell для последующих аргументов (подробнее об этом ниже);
  • \" сохраняет двойные кавычки для plink;
  • \' экранирует одинарные кавычки для удалённой оболочки.

Вариант 2 – комбинировать обратные апострофы и слэши:

plink -load "FIS-S-LBX-PGS-202-a" -batch psql -c \"select … name = 'server_version';`"

Но это выглядит сложнее (особенно если в запросе есть переменные).

Совет: Всегда проверяйте, как команда выглядит <strong>после обработки plink</strong>. Добавьте параметр -v (verbose) к psql или посмотрите логи PostgreSQL – там отображается «сырой» запрос.

Почему не работают другие способы?

  • Если использовать –% без экранирования, PowerShell передаст кавычки «как есть», но plink снимет их до передачи в psql.
  • Попытка обернуть весь запрос в одинарные кавычки вызовет конфликт с синтаксисом PowerShell.

Пример рабочей команды с комментариями:

plink -load "FIS-S-LBX-PGS-202-a" -batch psql -c --% \"SELECT 'версия_сервера' AS result WHERE '1' = \'1\';\"

Здесь:

  • –% отключает интерпретацию PowerShell после этого места;
  • \” – экранированные двойные кавычки для plink;
  • \’1\’ – экранированные одинарные кавычки для SQL.

Если всё сделано правильно, удалённый psql получит корректный запрос:

SELECT 'версия_сервера' AS result WHERE '1' = '1';

Частые ошибки и как их избежать

1. Лишние пробелы после -c:

Неправильно:

psql -c "select …"

Правильно:

powershell
psql -c"select …"

(Пробел после -c заставляет psql обрабатывать следующий аргумент как имя базы данных).

2. Непоследовательное экранирование:

Если часть кавычек экранирована, а часть нет, запрос «развалится» на удалённой стороне. Используйте единый стиль для всей команды.

3. Игнорирование версий plink/psql:

В старых версиях plink (до 0.76) могут быть баги с обработкой аргументов. Обновитесь командой:

choco upgrade putty -y

И напоследок: если команда всё равно не работает, попробуйте собрать её «снизу вверх» – сначала проверьте SQL в psql напрямую, затем добавьте plink, и только потом – обёртку в PowerShell. Это помогает локализовать слой, где происходит ошибка.

P.S. Кстати, аналогичные проблемы возникают при работе с awk или sed через SSH – принцип многоуровневого экранирования там тот же. Не стесняйтесь экспериментировать с кавычками и слэшами, даже если кажется, что вариантов больше нет.

Новое
Интересное