Як 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: такого файлу чи каталогу немає


Тоді як я можу це налаштувати?
Джаніт

@ 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'

Я хочу призначити /var/www/test.com/backupзмінну під назвою "BACKUPDEST", і cd $BACKUPDESTви можете мені сказати, як це зробити?
Джаніт

1
Де ви призначите значення BACKUPDEST? Якщо це відбувається на віддаленій стороні, просто включіть його в сценарій нормально. Якщо ви хочете встановити його локально (наприклад, обчислити його в локальному скрипті або передати його як аргумент командного рядка), ви можете змінити перший рядок на напр. "cd $BACKUPDEST"' ;- оболонка розширює подвійну котировку, зберігає одноцитовану. частина неушкоджена, об'єднує два і передає результат як останній аргумент 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, виконується та остаточно видаляється.

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


Не потрібно також зберігати скрипт у тимчасовий файл, просто передайте його у відповідну оболонку, тобто ssh user@host bash <myscript.sh.
pt314

2
Майте на увазі, що перенаправлення працює лише тоді, коли сам сценарій не намагається прочитати зі стандартного вводу.
чепнер

@ pt314 Так, але дивіться Pipe-скрипт у 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}виразі, але я вирішую їх додати.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.