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 ทำไมถึงเป็นเช่นนั้น คำสั่งอยู่ในเครื่องหมายคำพูด
การแข่งขัน Lightness ใน Orbit

2
$( )ส่วนหนึ่งของคำสั่งจะถูกดำเนินการโดยเปลือกท้องถิ่นก่อนที่จะได้เริ่มต้นsshคำสั่ง มันเป็นความจริงทั้งเมื่อ$( )ยืนอยู่คนเดียวและเมื่อถูกล้อมรอบด้วย"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คุณสามารถบอกวิธีการทำสิ่งนี้กับฉันได้ไหม
Janith

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จะไม่ทำงานตั้งแต่globbingเป็นเพียงสิ่งที่เกิดขึ้นในระบบท้องถิ่น นอกจากนี้ยังใช้lsสำหรับไฟล์ที่นับไม่ได้เป็นความคิดที่ดีเพราะมันยังส่งกลับรายการเช่น., ..และไดเรกทอรี


ฉันพบข้อผิดพลาด syntax error near unexpected token elif',elif [ $(ls -t |awk 'NR >3'|xargs rm -f) ]; then'
Janith

มี;คำสั่ง if-กำปั้นที่หายไป แต่ฉันไม่ได้แก้ไขสคริปต์ของคุณ! มีปัญหาอื่น ๆ อีกมากมายเช่นls *.tgz
Simon Sudler

1
"ls -t * .tgz จะใช้งานไม่ได้เนื่องจากการวนรอบกำลังเกิดขึ้นกับระบบโลคอลเท่านั้น" - มันจะเกิดขึ้นในระบบระยะไกลหากคุณหลบหนีออกมาอย่างถูกต้อง เวอร์ชันเดิมมี$(...)สายอักขระเปล่าภายในสตริงที่มีเครื่องหมายคำพูดคู่ดังนั้นโลคัลเชลล์จะประเมินค่า การหนีมันดัง\$(...)ที่คุณแสดงในตัวอย่างแรกหรือใช้เครื่องหมายอัญประกาศล้อมรอบสคริปต์ทั้งหมดป้องกันเชลล์โลคัลไม่ให้ขยายดังนั้นจึงส่งไปยังระบบรีโมตจะถูกขยายโดยเชลล์ระยะไกลและสคริปต์ทำงาน ตามที่ตั้งใจไว้.
pt314

นี่เป็นสถานการณ์ที่ฉันขอแนะนำให้ใช้เครื่องหมายคำพูดเดี่ยวแทนที่จะเป็นเอกสารที่นี่ แม้ว่าคุณจะมีตัวแปรบางอย่างที่คุณต้องการแทรกคุณก็ยังดีกว่าด้วยการใช้สตริงที่มีเครื่องหมายคำพูดเดี่ยวและprintfเพื่อขยายตัวแปร - ซึ่งยังคงจัดการได้ง่ายกว่าการพยายามหลีกเลี่ยงตัวแปรเชลล์ทั้งหมด นอกจากนี้มันจะหลีกเลี่ยงไม่จำเป็นต้องใช้catเพียงเพื่อรวบรวมเอกสารนี่เป็นตัวแปร!
Daniel Pryden

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
อย่างไรก็ตามโปรดทราบว่าการเปลี่ยนเส้นทางนั้นจะใช้งานได้เมื่อสคริปต์ไม่พยายามอ่านจากอินพุตมาตรฐาน
chepner


ใช่การเปลี่ยนเส้นทางมีข้อ จำกัด นอกจากนี้ยังหลีกเลี่ยงปัญหาด้านความปลอดภัยที่เกี่ยวข้องกับการเขียน (และดำเนินการ) ไฟล์ชั่วคราวที่ตั้งชื่อได้ หากคุณใช้สคริปต์ชั่วคราว (ไม่ว่าจะโดยทางเลือกหรือความจำเป็น) ให้สร้างโดยใช้สคริปต์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}แสดงออก แต่ฉันตัดสินใจที่จะเพิ่ม
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.