Передать значение переменной в сложную команду - PullRequest
0 голосов
/ 17 апреля 2019

Я пытаюсь передать значение из строковой переменной в сложную команду bash, и у меня возникла проблема с этим. Когда я выполняю команды:

files=($(ls abc*))

for file in "${files[@]}"
do
    scp $file user@domain.com:~
    ssh user@domain.com 'PGPASSWORD="abcd" psql -h domain.com -U user -d dbb -f ~/$file'
    ssh user@domain.com 'rm ~/$file'
done

Я получаю некоторые ошибки. Только 'scp' выполняется правильно, но строки после них не работают. Я вижу "отсутствует параметр для psql -f" и "файл не найден / невозможно удалить словарь" В чем проблема с этим кодом? Как передать строковое значение в команды ssh?

Ответы [ 3 ]

3 голосов
/ 17 апреля 2019

Одинарные кавычки не позволят удаленной оболочке получить вашу переменную. Не используйте ls в сценариях. Заключите в кавычки свои переменные. Используйте http://shellcheck.net/, прежде чем запрашивать рецензию у человека.

for file in abc*
do
    scp "$file" user@domain.com:~
    ssh user@domain.com "PGPASSWORD='abcd' psql -h domain.com -U user -d dbb -f ~/'$file'; 
        rm ~/'$file'"
done

ЦитированиеЭто немного сложнее, и не будет работать, если имена ваших файлов могут содержать, например, буквальные одинарные кавычки.Двойные кавычки приводят к расширению локальной оболочки $file;затем расширенное значение заключено в одинарные кавычки, чтобы предотвратить дальнейшее манипулирование удаленным ssh.

Я вынул массив, потому что он, похоже, не служил какой-либо полезной цели.Если вы хотите массив, назначьте его просто с подстановочным знаком:

files=(abc*)
2 голосов
/ 17 апреля 2019

В чем проблема с этим кодом? Как передать строковое значение в команды ssh?

Одно из важных различий между цитированием в двойных кавычках (") и цитированием в одинарных кавычках (') заключается в том, что последний подавляет расширение параметра. Вам нужно либо переместить $file за пределы кавычек, либо изменить стиль двойной кавычки. Кроме того, однако, вы должны правильно процитировать расширения $file, иначе вы оставите себя открытым для поломки или даже неприятных сюрпризов в случае необычных имен файлов, таких как те, которые содержат пробелы.

Более того, тот факт, что вы форматируете командную строку, которая будет обрабатываться другой оболочкой, добавляет некоторые сложности. Самый простой способ убедиться, что вы получаете право цитирования, - это использовать встроенную в bash printf, чья опция форматирования %q служит именно для того, чтобы заключить в кавычки произвольную строку, чтобы результат можно было перечитать как ввод в оболочку для воспроизведения та же строка. В целом, вот моя рекомендация для цикла:

for file in "${files[@]}"
do
    scp "${file}" user@domain.com:~
    ssh user@domain.com "PGPASSWORD='abcd' psql -h domain.com -U user -d dbb -f $(printf %q "~/${file}")"
    ssh user@domain.com "rm $(printf %q "~/${file}")"
done
0 голосов
/ 17 апреля 2019

Вместо копирования каждого файла на удаленный хост для выполнения, настройте SSH-туннель и запустите psql локально , подключившись к удаленной базе данных через туннель.

ssh -N -L 12345:domain.com:$DB_PORT user@domain.com & ssh_pid=$!

files=(abc*)

for file in "${files[@]}"
do
    psql -p 12345 -U user -d dbb -f "$file"
done

kill "$ssh_pid"

Это дает дополнительное преимущество - не раскрывать пароль базы данных, поскольку вы можете просто ввести его вручную или использовать локальный файл .pgpass.

Обратите внимание, что туннель необходим только в том случае, если удаленная база данных не настроенапринимать сетевые подключения.

...