Bagaimana SSH dapat bekerja dengan kondisi if?

8

Saya memiliki ifpernyataan untuk menghitung file dan menghapus semua kecuali tiga file terbaru. Tapi saya ingin menjalankan perintah ini dari jarak jauh. Bagaimana saya bisa bergabung sshdengan suatu ifkondisi?

Saya mencoba ini tetapi tidak berhasil.

#!/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"

Kesalahan yang saya dapatkan:

ls: tidak dapat mengakses * .tgz: Tidak ada file atau direktori tersebut

Janith
sumber
Lalu bagaimana saya bisa menyesuaikan ini?
Janith
@ user68186 Kenapa begitu? Perintah dalam tanda kutip.
Lightness Races in Orbit
2
Bagian $( )dari perintah dieksekusi oleh shell lokal sebelum bahkan memulai sshperintah. Itu benar baik ketika $( )berdiri sendiri maupun ketika itu tertutup oleh "s. Namun jika $( )di dalam 's itu tidak akan dieksekusi oleh shell lokal.
kasperd

Jawaban:

3

CATATAN: sebenarnya ada dua lapisan untuk pertanyaan di sini. Salah satunya adalah 'Saya ingin menjalankan tugas non-sepele pada server jauh yang dapat diakses melalui SSH'. Yang lain adalah 'Saya sedang mencoba memberikan string yang kompleks ke sebuah perintah, dan argumennya akhirnya berbeda dari yang saya maksudkan.' Saya menjawab pertanyaan tingkat rendah tanpa membahas apakah pendekatan yang digunakan adalah "benar" (nyaman, tidak rentan kesalahan, aman, dll.) Untuk menyelesaikan yang tingkat tinggi. Seperti yang ditunjukkan oleh jawaban dan komentar lain, sangat mungkin tidak.

Baris perintah Anda sebagian besar benar; Anda hanya perlu mengubah sedikit penawaran.

Masalah utama adalah bahwa string yang dikutip ganda diperluas oleh shell lokal Anda, dan $(...)bagian - bagian tersebut akan dievaluasi pada sistem lokal Anda. Untuk meneruskannya ke sistem jarak jauh, Anda harus melampirkan script dalam tanda kutip tunggal.

Anda juga memiliki beberapa tanda kutip yang tertanam. Dalam naskah asli Anda, ada argumen untuk keduanya echo; jika Anda mengubah kutipan luar menjadi kutipan tunggal, itu akan menjadi skrip awk. Ini secara efektif menghasilkan tanda kutip yang dihilangkan, yang tidak mengganggu echohuruf s, tetapi akan mengacaukan skrip awk, karena tanda yang lebih besar dari itu akan menjadi pengalihan output. Jadi setelah Anda mengubah tanda kutip luar menjadi tanda kutip tunggal, ubah tanda kutip menjadi dua.

Ini adalah skrip Anda dengan kutipan tetap. Script mungkin memiliki masalah lain, saya baru saja memperbaiki sintaks.

#!/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
sumber
Saya ingin menetapkan /var/www/test.com/backupvariabel yang disebut "BACKUPDEST" dan cd $BACKUPDESTdapatkah Anda memberi tahu saya cara melakukan ini?
Janith
1
Di mana Anda menetapkan nilainya BACKUPDEST? Jika itu terjadi di sisi jarak jauh, cukup sertakan dalam skrip secara normal. Jika Anda ingin menyetelnya secara lokal (mis. Menghitungnya dalam skrip lokal, atau meneruskannya sebagai argumen baris perintah), Anda dapat mengubah baris pertama menjadi mis. "cd $BACKUPDEST"' ;- shell memperluas bagian yang dikutip ganda, membuat yang dikutip tunggal bagian utuh, menggabungkan keduanya dan meneruskan hasilnya sebagai argumen terakhir ssh.
pt314
Uh, buat itu "cd '$BACKUPDEST'"' ;jika path dalam variabel itu berisi beberapa keanehan (misalnya spasi). Sekali lagi: hanya mengatakan bahwa itu bisa dilakukan dengan cara ini, bukan berarti harus dilakukan dengan cara ini.
pt314
10

Ya, Anda dapat menjalankan skrip yang rumit melalui 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"

Contoh ini menggunakan dokumen bash di sini untuk menghasilkan string perintah. Dalam kasus apa pun, melewatkan skrip melalui ssh adalah kesalahan, karena penawaran dan pelepasan variabel sulit (ingat backslash sebelum perintah). Jika skrip menjadi terlalu rumit, lebih baik menyalinnya melalui scpdan kemudian menjalankannya pada host target.

Saya tidak mencoba memperbaiki skrip Anda , tetapi berikut ini adalah contoh cara penghitungan dan penghapusan host jarak jauh:

#!/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"

Ini ls -t *.tgztidak akan berfungsi, karena globbing hanya terjadi pada sistem lokal. Juga menggunakan lsuntuk menghitung file bukan ide yang baik, karena juga mengembalikan entri seperti ., ..dan direktori.

Simon Sudler
sumber
Saya mendapat beberapa kesalahan. syntax error near unexpected token elif',elif [ $(ls -t |awk 'NR >3'|xargs rm -f) ]; then'
Janith
Ada yang ;hilang dalam pernyataan jika kepalan itu. Tapi saya tidak memperbaiki skrip Anda! Ada banyak lagi masalah misalnyals *.tgz
Simon Sudler
1
"Ls -t * .tgz tidak akan berfungsi, karena globbing hanya terjadi pada sistem lokal." - itu akan terjadi pada sistem jarak jauh jika Anda menghindarinya dengan benar. Versi asli telanjang $(...)di dalam string yang dikutip ganda, sehingga shell lokal mengevaluasinya. Melarikannya \$(...)seperti yang Anda tunjukkan pada contoh pertama, atau menggunakan tanda kutip tunggal di sekitar keseluruhan skrip, mencegah shell lokal untuk memperluasnya, sehingga ia dikirim ke sistem jarak jauh, itu diperluas oleh shell jauh, dan skrip berfungsi sebagaimana dimaksud.
pt314
Ini adalah situasi di mana saya akan sangat merekomendasikan menggunakan tanda kutip tunggal daripada dokumen di sini. Bahkan jika Anda memiliki beberapa variabel yang ingin Anda sisipkan, Anda lebih baik menggunakan string yang dikutip tunggal dan printfuntuk memperluas variabel - itu masih akan lebih mudah untuk dikelola daripada mencoba keluar dari semua variabel shell di seluruh. Juga, itu akan menghindari keharusan menggunakan cathanya untuk menangkap dokumen di sini menjadi variabel!
Daniel Pryden
4

Saya pikir seluruh hal mengutip yang rumit ini adalah bukti yang cukup untuk tidak menggunakannya tetapi menggunakan skrip. Jika Anda ingin menghindari beberapa sshkoneksi, pipa skrip ke host lain dan biarkan dijalankan di sana dalam satu perintah:

File lokal, katakan 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

Kemudian:

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

Atau (menghindari penggunaan kucing yang tidak berguna ):

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

Ini mem-pipe script lokal myscript.shke sisi yang jauh di mana ia diarahkan ke file (sementara) /tmp/ms.sh, dieksekusi, dan akhirnya dihapus.

Catatan: Saya tidak memeriksa kesalahan naskah asli tetapi hanya ingin menunjukkan ide. Tidak ada kutipan rawan kesalahan diperlukan dan semua perintah dalam skrip dieksekusi di sisi jarak jauh.

PerlDuck
sumber
Tidak perlu menyimpan skrip ke file sementara juga, cukup pipa itu ke shell yang sesuai, yaitu ssh user@host bash <myscript.sh.
pt314
2
Perlu diingat, bahwa pengalihan hanya berfungsi ketika skrip itu sendiri tidak mencoba membaca dari input standar.
chepner
@ pt314 Ya, tetapi lihat Memotong skrip ke bash?
PerlDuck
Ya, pengalihan memiliki keterbatasan. Itu juga menghindari banyak masalah keamanan yang terkait dengan penulisan (dan kemudian mengeksekusi) file sementara yang dapat diprediksi namanya. Jika Anda memang menggunakan skrip sementara (baik karena pilihan atau kebutuhan), buat menggunakan mktempatau sesuatu yang serupa amannya.
pt314
3

Saya lebih suka menempatkan script pada contoh jarak jauh dan hanya menjalankannya melalui ssh, tapi di sini adalah saran saya bagaimana ini bisa dilakukan dengan cara yang Anda inginkan:

#!/bin/bash

HOST='[email protected]'
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

Catatan:

  • Ekspresi bersyarat -ltdigantikan oleh -le.
  • eval - membangun perintah dengan menggabungkan argumen.
  • Saya tidak yakin apakah kami benar-benar membutuhkan kutipan tambahan ini \"di dalam $COMMAND{1..3}ekspresi, tetapi saya memutuskan untuk menambahkannya.
pa4080
sumber