Как SSH может работать с условием if?

8

У меня есть ifзаявление для расчета файлов и удаления всех, кроме последних трех файлов. Но я хочу запустить эту команду удаленно. Как я могу сочетать sshс ifусловием?

Я попробовал это, но безуспешно.

#!/bin/bash

ssh -t  test@192.168.94.139 "cd /var/www/test.com/backup ;

if [ $(ls  | wc -l) -lt 3  ]
then
    echo "Less"
else [ $(ls -t *.tgz|awk 'NR >3'|xargs rm -f) ]
    echo "deleted"
fi"

Ошибка, которую я получил:

ls: не может получить доступ * .tgz: нет такого файла или каталога

Janith
источник
Тогда как я могу настроить это?
Джанит
@ user68186 Почему это? Команда в кавычках.
Гонки
2
$( )Часть команды выполняются локальной оболочкой , прежде чем он даже начинает sshкоманду. Это верно как в $( )одиночку, так и в том случае, если оно заключено в "s. Однако если бы он $( )был внутри 's, он не был бы выполнен локальной оболочкой.
kasperd

Ответы:

3

ПРИМЕЧАНИЕ: на самом деле здесь два вопроса. Один из них: «Я хочу выполнить нетривиальную задачу на удаленном сервере, доступном через SSH». Другой - «Я пытаюсь передать сложную строку команде, и аргумент в конечном итоге отличается от того, что я намеревался». Я отвечаю на вопрос низкого уровня, не обсуждая, является ли используемый подход «правильным» (удобный, не подверженный ошибкам, безопасный и т. Д.) Для решения высокого уровня. Как указано в других ответах и ​​комментариях, вполне возможно, что нет.

Ваша командная строка в основном правильная; вам нужно только немного изменить цитату.

Основная проблема заключается в том, что строки в двойных кавычках расширяются вашей локальной оболочкой, и поэтому $(...)части будут оцениваться в вашей локальной системе. Чтобы передать их в удаленную систему, вы должны заключить скрипт в одинарные кавычки.

У вас также есть некоторые кавычки. В вашем оригинальном сценарии есть аргументы для двух echos; если вы измените внешнюю цитату на одинарную, то это будет скрипт awk. Это эффективно приводит к тому, что кавычки опускаются, что не беспокоит echos, но это испортит скрипт awk, так как знак больше чем станет перенаправлением вывода. Поэтому после изменения внешних кавычек на одинарные кавычки измените их на двойные.

Это ваш скрипт с исправленной цитатой. У скрипта могут быть другие проблемы, я просто исправил синтаксис.

#!/bin/bash

ssh -t  test@192.168.94.139 'cd /var/www/test.com/backup ;

if [ $(ls  | wc -l) -lt 3  ]
then
  echo "Less"
else [ $(ls -t *.tgz|awk "NR >3"|xargs rm -f) ]
  echo "deleted"
fi'
pt314
источник
Я хочу присвоить /var/www/test.com/backupпеременную с именем "BACKUPDEST" и cd $BACKUPDESTможете ли вы сказать мне, как это сделать?
Джанит
1
Где вы присваиваете значение BACKUPDEST? Если это происходит на удаленной стороне, просто включите его в скрипт как обычно. Если вы хотите установить его локально (например, рассчитать его в локальном скрипте или передать в качестве аргумента командной строки), вы можете изменить первую строку на например: "cd $BACKUPDEST"' ;- оболочка расширяет часть в двойных кавычках, сохраняет одинарные в кавычках part intact, объединяет их и передает результат в качестве последнего аргумента ssh.
pt314
Сделайте это "cd '$BACKUPDEST'"' ;в случае, если путь в этой переменной содержит некоторые странности (например, пробел). Опять же: просто сказать, что это можно сделать таким образом, а не то, что это должно быть сделано таким образом.
pt314
10

Да, вы можете выполнить сложные сценарии через ssh

#!/bin/bash -e

ssh_cmd="$(cat <<-EOF
    cd /var/www/test.com/backup;

    if [ \$(ls  | wc -l) -lt 3  ]; then
        echo "less"
    elif [ \$(ls -t *.tgz|awk 'NR >3'|xargs rm -f) ]; then
        echo "deleted"
    else
        echo "something else"
    fi
EOF
)"

ssh -t test@192.168.94.139 "$ssh_cmd"

В этом примере для создания командной строки используется документ bash here . В любом случае, передача сценариев через ssh подвержена ошибкам, поскольку сложное цитирование и экранирование переменных затруднены (обратите внимание на обратную косую черту перед командами). Если скрипт становится слишком сложным, лучше скопировать его через, scpа затем выполнить на целевом хосте.

Я не пытался исправить ваш скрипт , но вот пример того, как может работать подсчет и удаление на удаленном хосте:

#!/bin/bash -e

tmp_dir="$(mktemp -d)"

ssh_cmd="$(cat <<-EOF
    cd "$tmp_dir"

    cnt_tgz=(\$(find . -type f -name "*.tgz"))
    if [[ \${#cnt_tgz[@]} -lt 3 ]]; then
        echo "less"
    else
        rm "\${cnt_tgz[@]}"
        echo "deleted"
    fi
EOF
)"

touch "$tmp_dir/1.tgz"
ssh -t localhost "$ssh_cmd"
touch "$tmp_dir/2.tgz" "$tmp_dir/3.tgz"
ssh -t localhost "$ssh_cmd"

Это ls -t *.tgzне сработает, поскольку глобализация происходит только в локальной системе. Кроме того, используя lsдля подсчета файлов не является хорошей идеей, поскольку она также возвращает запись , как ., ..и каталоги.

Саймон Судлер
источник
Я получил несколько ошибок. syntax error near unexpected token elif',elif [ $(ls -t |awk 'NR >3'|xargs rm -f) ]; then'
Джанит
В ;первом заявлении пропало без вести. Но я не исправил тебе скрипт! Есть еще много вопросов, напримерls *.tgz
Саймон Судлер
1
«Ls -t * .tgz не будет работать, поскольку глобализация происходит только в локальной системе». - это произойдет в удаленной системе, если вы избежите этого правильно. Первоначальная версия была обнажена $(...)внутри строки в двойных кавычках, поэтому локальная оболочка оценила ее. Экранирование, как \$(...)показано в первом примере, или использование одинарных кавычек вокруг всего сценария предотвращает его расширение локальной оболочкой, поэтому он отправляется в удаленную систему, расширяется удаленной оболочкой и сценарий работает. как предполагалось.
pt314
Это такая ситуация, когда я настоятельно рекомендую использовать одинарные кавычки вместо документа здесь. Даже если у вас есть какие-то переменные, которые вы хотите вставить, лучше использовать строку в одинарных кавычках и printfрасширять переменные - с этим все равно будет легче справиться, чем пытаться экранировать все переменные оболочки. Кроме того, это позволит избежать необходимости использовать catпросто для захвата здесь документ в переменную!
Даниэль Приден
4

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

Локальный файл, скажем myscript.sh:

cd /var/www/test.com/backup;

if [ $(ls  | wc -l) -lt 3  ]; then
    echo "less"
elif [ $(ls -t *.tgz|awk 'NR >3'|xargs rm -f) ]; then
    echo "deleted"
else
    echo "something else"
fi

Затем:

cat myscript.sh | \
    ssh -t test@192.168.94.139 \
    "cat - > /tmp/ms.sh && sh /tmp/ms.sh && rm /tmp/ms.sh"

Или (избегая бесполезного использования кошки ):

ssh -t test@192.168.94.139 \
    "cat - > /tmp/ms.sh && sh /tmp/ms.sh && rm /tmp/ms.sh" \
    < myscript.sh

Это передает локальный сценарий myscript.shна удаленную сторону, где он перенаправляется во (временный) файл /tmp/ms.sh, выполняется и, наконец, удаляется.

Примечание: я не проверял оригинальный скрипт на наличие ошибок, а просто хотел показать идею. Нет необходимости в цитировании, склонном к ошибкам, и все команды в скрипте выполняются на удаленной стороне.

PerlDuck
источник
Нет необходимости , чтобы сохранить скрипт во временный файл , либо просто труба в подходящую оболочку, то есть ssh user@host bash <myscript.sh.
pt314
2
Имейте в виду, однако, что перенаправление работает только тогда, когда сам скрипт не пытается читать со стандартного ввода.
chepner
@ pt314 Да, но вы видите, как передать скрипт в bash?
PerlDuck
Да, перенаправление имеет свои ограничения. Это также позволяет избежать проблем с безопасностью, связанных с записью (а затем выполнением) временного файла с предсказуемым именем. Если вы используете временный скрипт (по выбору или по необходимости), создайте его, используя mktempили что-то подобное, безопасное.
pt314
3

Я бы предпочел разместить скрипт на удаленном экземпляре и просто выполнить его через ssh, но вот мое предложение, как это можно сделать так, как вы хотите:

#!/bin/bash

HOST='test@192.168.94.139'
REMOTE_PATH='/var/www/test.com/backup'

COMMAND1="ssh \"$HOST\" 'ls \"$REMOTE_PATH\" | wc -l'"
COMMAND2="ssh \"$HOST\" 'ls -t \"$REMOTE_PATH\"/*.tgz'"
COMMAND3="xargs ssh \"$HOST\" rm -rf"

if [[ $(eval "$COMMAND1") -le 3 ]]
then
    echo "Less"
else
    eval "$COMMAND2" | awk 'NR > 3' | eval "$COMMAND3" && echo "Deleted"
fi

Ноты:

  • Условное выражение -ltзаменяется на -le.
  • eval - построить команду путем объединения аргументов.
  • Я не уверен, что нам действительно нужны эти дополнительные цитаты \"в $COMMAND{1..3}выражении, но я решил добавить их.
pa4080
источник