BASH: многострочные команды по SSH

Автор: | 27/01/2016

terminalЕсть несколько вариантов как выполнить несколько команд на удаленном сервере по SSH.

Первый – используя разделитель “;“:

$ ssh -p 2222 [email protected] ls /opt/; ls -1 /var
[email protected]'s password:
mailstat
pflogsumm-1.1.1
pflogsumm-1.1.1.tar.gz
Keychains
...

Второй – используя одинарные кавычки:

$ ssh -p 2222 [email protected] '
> ls -l /opt
> ls -l /var
> '
[email protected]'s password:
total 52
drwxr-xr-x 2 root root  4096 Aug 22  2014 mailstat
drwxr-xr-- 2 root root  4096 Jul 12  2014 pflogsumm-1.1.1
-rw-r--r-- 1 root root 44627 Apr  6  2007 pflogsumm-1.1.1.tar.gz
total 72
drwxr-xr-x. 10 root root 4096 Dec 25 15:56 cache
...
drwxr-xr-x  10 root root 4096 Dec 15 17:51 www
drwxr-xr-x.  2 root root 4096 Sep 23  2011 yp

Но в таком случае не будут раскрыты переданные переменные:

$ var="VALUE"
$ ssh -p 2222 [email protected] '
> echo $HOME
> echo $var
> '
[email protected]'s password:
/home/user

Локальная переменная на удаленном сервере $HOME работает, а вот значение переданной переменной – нет.

Добавляем bash -c и двойные кавычки:

$ var="VALUE"
$ ssh -p 2222 [email protected] "bash -c '
> echo $HOME
> echo $var
> '"
[email protected]'s password:
/Users/local.user
VALUE

Обратите внимание на значение $HOME - /Users/local.user.

Следующий способ – перенаправить список команд из файла:

$ cat bashlines
var="VALUE"
ls -l /opt
echo $HOME
echo $var
$ ssh -p 2222 [email protected] < bashlines
Pseudo-terminal will not be allocated because stdin is not a terminal.
[email protected]'s password:
total 52
drwxr-xr-x 2 root root  4096 Aug 22  2014 mailstat
drwxr-xr-- 2 root root  4096 Jul 12  2014 pflogsumm-1.1.1
-rw-r--r-- 1 root root 44627 Apr  6  2007 pflogsumm-1.1.1.tar.gz
/home/setevoy
VALUE

Или – используя heredoc:

$ ssh -p 2222 [email protected] <<EOF
> var="VALUE"
> ls -l /opt
> echo $HOME
> echo $var
> EOF
Pseudo-terminal will not be allocated because stdin is not a terminal.
[email protected]'s password:
total 52
drwxr-xr-x 2 root root  4096 Aug 22  2014 mailstat
drwxr-xr-- 2 root root  4096 Jul 12  2014 pflogsumm-1.1.1
-rw-r--r-- 1 root root 44627 Apr  6  2007 pflogsumm-1.1.1.tar.gz
/Users/local.user
VALUE

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

“Pseudo-terminal will not be allocated because stdin is not a terminal.”

используйте опцию -T:

$ man ssh | grep "-T"
     -T      Disable pseudo-tty allocation.
             warded to host on port over the secure channel.  Implies -N, -T,

Например:

$ ssh -T -p 2222 [email protected] < bashlines
[email protected]'s password:
total 52
drwxr-xr-x 2 root root  4096 Aug 22  2014 mailstat
drwxr-xr-- 2 root root  4096 Jul 12  2014 pflogsumm-1.1.1
-rw-r--r-- 1 root root 44627 Apr  6  2007 pflogsumm-1.1.1.tar.gz
/home/setevoy
VALUE

И пример bash-скрипта деплоя (точнее – дестроя перед деплоем 🙂 ), в котором используются многострочные команды SSH:

#!/usr/bin/env bash

RSA_KEYS=".ssh/id_rsa"
USER="knife"
HOST="$1"

JBOSS_HOME="/opt"
JBOSS_WORK_DIR="jboss"
JBOSS_DEPLOY_DIR_NAME="jboss-eap-6.3"
JBOSS_DEPLOY_ARCH_NAME="$JBOSS_DEPLOY_DIR_NAME.zip"

me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"

echo -e "n[$me] JBoss destroy started.n"

echo -e "Root directory: $JBOSS_HOME"
echo -e "JBoss home: $JBOSS_WORK_DIR"
echo -e "JBoss temp dir: $JBOSS_DEPLOY_DIR_NAME"
echo -e "JBoss deploy archive: $JBOSS_DEPLOY_ARCH_NAMEn"

if [ -z "$HOST" ]; then
	echo -e "ERROR: HOST must be defined with first argument! Exit.n"
	exit 1
fi

if [ -e "$RSA_KEYS" ]; then
	chmod 600 "$RSA_KEYS"
else
	echo -e "ERROR: No private RSA key found! Exit.n"
	exit 1
fi

echo -e "Worknig on $HOST under user $USER.n"

rm_work_dir () {
	ssh -t -t -oStrictHostKeyChecking=no -i "$RSA_KEYS" "$USER@$HOST" "bash -c '
		if [ -d "$JBOSS_HOME/$JBOSS_WORK_DIR" ]; then
			echo -e "Removing $JBOSS_HOME/$JBOSS_WORK_DIR:"
			sudo rm -rfv "$JBOSS_HOME/$JBOSS_WORK_DIR"
		else
			echo -e "$JBOSS_HOME/$JBOSS_WORK_DIR not found, skipping..."
		fi
	'"
}

rm_deploy_dir () {
	ssh -t -t -oStrictHostKeyChecking=no -i "$RSA_KEYS" "$USER@$HOST" "bash -c '
		if [ -d "$JBOSS_HOME/$JBOSS_DEPLOY_DIR_NAME" ]; then
			echo -e "Removing $JBOSS_HOME/$JBOSS_DEPLOY_DIR_NAME:"
			sudo rm -rfv "$JBOSS_HOME/$JBOSS_DEPLOY_DIR_NAME"
		else
			echo -e "$JBOSS_HOME/$JBOSS_DEPLOY_DIR_NAME not found, skipping..."
		fi
	'"
}

rm_deploy_arch () {
	ssh -t -t -oStrictHostKeyChecking=no -i "$RSA_KEYS" "$USER@$HOST" "bash -c '
		if [ -e "$JBOSS_HOME/$JBOSS_DEPLOY_ARCH_NAME" ]; then
			echo -e "Removing $JBOSS_HOME/$JBOSS_DEPLOY_ARCH_NAME:"
			sudo rm -fv "$JBOSS_HOME/$JBOSS_DEPLOY_ARCH_NAME"
		else
			echo -e "$JBOSS_HOME/$JBOSS_DEPLOY_ARCH_NAME not found, skipping..."
		fi
	'"
}

if rm_work_dir; then
	echo -e "n[$me] Work dir cleaned.n"
fi

if rm_deploy_dir; then
	echo -e "n[$me] Deploy directory cleaned.n"
fi

if rm_deploy_arch; then
	echo -e "n[$me] Deploy archive cleaned."
fi

echo -e "n[$me] JBoss destroy completed successfully.n"
exit 0