Làm thế nào SSH có thể làm việc với một điều kiện if?


8

Tôi có một iftuyên bố để tính toán các tệp và xóa tất cả ngoại trừ ba tệp mới nhất. Nhưng tôi muốn chạy lệnh này từ xa. Làm thế nào tôi có thể kết hợp sshvới một ifđiều kiện?

Tôi đã thử điều này nhưng không thành công.

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

Lỗi tôi nhận được:

ls: không thể truy cập * .tgz: Không có tệp hoặc thư mục như vậy


Sau đó, làm thế nào tôi có thể tùy chỉnh này?
Janith

@ user68186 Tại sao vậy? Lệnh nằm trong dấu ngoặc kép.
Các cuộc đua nhẹ nhàng trong quỹ đạo

2
Một $( )phần của lệnh được thực thi bởi shell cục bộ trước khi nó bắt đầu sshlệnh. Điều đó đúng cả khi $( )đứng một mình cũng như khi nó được bao quanh bởi "s. Tuy nhiên, nếu $( )ở bên trong ', nó sẽ không được thực thi bởi trình bao cục bộ.
kasperd

Câu trả lời:


3

LƯU Ý: trên thực tế có hai lớp cho câu hỏi ở đây. Một là 'Tôi muốn thực thi một nhiệm vụ không hề nhỏ trên một máy chủ từ xa có thể truy cập thông qua SSH'. Cái khác là 'Tôi đang cố gắng truyền một chuỗi phức tạp cho một lệnh và đối số kết thúc khác với những gì tôi dự định.' Tôi đang trả lời câu hỏi cấp thấp mà không thảo luận về cách tiếp cận được sử dụng là "đúng" (thuận tiện, không dễ bị lỗi, an toàn, v.v.) để giải quyết vấn đề cấp cao. Như được chỉ ra bởi các câu trả lời và ý kiến ​​khác, nó hoàn toàn có thể không.

Dòng lệnh của bạn chủ yếu là đúng; bạn chỉ phải thay đổi trích dẫn một chút.

Vấn đề chính là các chuỗi trích dẫn kép được mở rộng bởi trình bao cục bộ của bạn và do đó các $(...)phần sẽ được đánh giá trên hệ thống cục bộ của bạn. Để chuyển chúng qua hệ thống từ xa, bạn phải đặt tập lệnh trong dấu ngoặc đơn.

Bạn cũng có một số dấu ngoặc kép nhúng. Trong kịch bản gốc của bạn, có các đối số cho hai echos; nếu bạn thay đổi trích dẫn bên ngoài thành dấu ngoặc đơn, nó sẽ là tập lệnh awk. Những kết quả này sẽ dẫn đến việc các dấu ngoặc kép bị bỏ qua, điều này không làm phiền đến echos, nhưng nó sẽ làm rối loạn tập lệnh awk, vì dấu lớn hơn sẽ trở thành chuyển hướng đầu ra. Vì vậy, sau khi bạn thay đổi dấu ngoặc kép bên ngoài thành dấu ngoặc đơn, hãy thay đổi dấu ngoặc kép thành dấu ngoặc kép.

Đây là kịch bản của bạn với trích dẫn cố định. Kịch bản có thể có vấn đề khác, tôi chỉ sửa lỗi cú pháp.

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

Tôi muốn gán /var/www/test.com/backupvào một biến có tên là "BACKUPDEST" và cd $BACKUPDESTbạn có thể cho tôi biết làm thế nào để làm điều này?
Janith

1
Bạn gán giá trị ở BACKUPDESTđâu? Nếu điều đó xảy ra ở phía xa, chỉ cần đưa nó vào tập lệnh một cách bình thường. Nếu bạn muốn đặt cục bộ (ví dụ: tính toán nó trong tập lệnh cục bộ hoặc chuyển nó thành đối số dòng lệnh), bạn có thể thay đổi dòng đầu tiên thành vd "cd $BACKUPDEST"' ;- shell mở rộng phần trích dẫn kép, giữ nguyên trích dẫn đơn một phần còn nguyên vẹn, nối hai phần và chuyển kết quả làm đối số cuối cùng ssh.
pt314

Uh, làm cho nó "cd '$BACKUPDEST'"' ;trong trường hợp đường dẫn trong biến đó chứa một số điều kỳ lạ (ví dụ như một khoảng trắng). Một lần nữa: chỉ nói rằng nó có thể được thực hiện theo cách này, không phải là nó nên được thực hiện theo cách này.
pt314

10

Có, bạn có thể thực hiện các tập lệnh phức tạp thông qua 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"

Ví dụ này sử dụng tài liệu bash here để tạo chuỗi lệnh. Trong mọi trường hợp, việc truyền tập lệnh qua ssh rất dễ bị lỗi, bởi vì việc trích dẫn và thoát các biến là khó khăn (lưu ý dấu gạch chéo ngược trước các lệnh). Nếu tập lệnh trở nên quá phức tạp, tốt hơn là sao chép nó qua scpvà sau đó thực thi nó trên máy chủ đích.

Tôi đã không cố gắng sửa tập lệnh của bạn , nhưng đây là một ví dụ về cách đếm và xóa trên máy chủ từ xa có thể hoạt động:

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

Các ls -t *.tgzsẽ không làm việc, kể từ khi globbing chỉ xảy ra trên hệ thống của địa phương. Ngoài ra sử dụng lscho các tập tin đếm không phải là một ý tưởng tốt, vì nó cũng trả về các mục như ., ..và thư mục.


Tôi có một số lỗi. syntax error near unexpected token elif',elif [ $(ls -t |awk 'NR >3'|xargs rm -f) ]; then'
Janith

Có một ;mất tích trong nắm đấm if-statement. Nhưng tôi đã không sửa cho bạn kịch bản! Còn nhiều vấn đề nữa, ví dụ:ls *.tgz
Simon Sudler

1
"Ls -t * .tgz sẽ không hoạt động, vì quá trình toàn cầu chỉ xảy ra trên hệ thống cục bộ." - nó sẽ xảy ra trên hệ thống từ xa nếu bạn thoát nó đúng cách. Phiên bản gốc đã khỏa thân $(...)bên trong một chuỗi trích dẫn kép, vì vậy trình bao cục bộ đã đánh giá nó. Thoát khỏi nó như \$(...)trong ví dụ đầu tiên, hoặc sử dụng các trích dẫn đơn xung quanh toàn bộ tập lệnh, ngăn shell cục bộ mở rộng nó, để nó được gửi đến hệ thống từ xa, nó được mở rộng bởi shell từ xa và script hoạt động như dự định.
pt314

Đây là loại tình huống mà tôi rất khuyến khích sử dụng dấu ngoặc đơn thay vì tài liệu ở đây. Ngay cả khi bạn có một số biến bạn muốn chèn, bạn vẫn nên sử dụng một chuỗi trích dẫn đơn và printfmở rộng các biến - điều đó vẫn sẽ dễ quản lý hơn là cố gắng thoát tất cả các biến shell trong suốt. Ngoài ra, nó sẽ tránh việc cần sử dụng catchỉ để chụp tài liệu ở đây thành một biến!
Daniel Pryden

4

Tôi nghĩ rằng toàn bộ công cụ trích dẫn phức tạp này là bằng chứng đủ để không sử dụng nó mà thay vào đó sử dụng một tập lệnh. Nếu bạn muốn tránh nhiều sshkết nối, chuyển tập lệnh sang máy chủ khác và để nó chạy ở đó trong một lệnh:

Tập tin cục bộ, nói 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

Sau đó:

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

Hoặc (tránh sử dụng mèo vô dụng ):

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

Điều này dẫn kịch bản cục bộ myscript.shđến phía xa nơi nó được chuyển hướng đến một tệp (tạm thời) /tmp/ms.sh, được thực thi và cuối cùng được gỡ bỏ.

Lưu ý: Tôi đã không kiểm tra tập lệnh gốc để tìm lỗi mà chỉ muốn hiển thị ý tưởng. Không có trích dẫn dễ bị lỗi là cần thiết và tất cả các lệnh trong tập lệnh được thực thi ở phía xa.


Không cần phải lưu tập lệnh vào một tệp tạm thời, chỉ cần đặt nó vào một vỏ phù hợp, tức là ssh user@host bash <myscript.sh.
pt314

2
Tuy nhiên, hãy nhớ rằng chuyển hướng chỉ hoạt động khi bản thân tập lệnh không cố đọc từ đầu vào tiêu chuẩn.
chepner


Vâng, chuyển hướng có những hạn chế của nó. Nó cũng tránh một loạt các vấn đề bảo mật liên quan đến việc viết (và sau đó thực thi) một tệp tạm thời có tên dự đoán. Nếu bạn sử dụng tập lệnh tạm thời (cho dù là do lựa chọn hay cần thiết), hãy tạo tập lệnh bằng cách sử dụng mktemphoặc một cái gì đó tương tự an toàn.
pt314

3

Tôi muốn đặt tập lệnh trên cá thể từ xa và chỉ thực thi nó thông qua ssh, nhưng đây là gợi ý của tôi về cách thực hiện điều này theo cách bạn muốn:

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

Ghi chú:

  • Biểu thức điều kiện -ltđược thay thế bằng -le.
  • eval - xây dựng lệnh bằng cách nối các đối số.
  • Tôi không chắc chúng ta có thực sự cần những trích dẫn thêm này \"trong $COMMAND{1..3}biểu thức không, nhưng tôi quyết định thêm chúng.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.