Shell Script: การสร้างตัวแปรพร้อมตัวเลือกภายใน


11

ฉันมีคำสั่ง rsync พร้อมพารามิเตอร์ต่อไปนี้:

rsync -avz --{partial,stats,delete,exclude=".*"}

ฉันต้องการวางพารามิเตอร์นั้นไว้ในตัวแปรเพื่อนำมาใช้ใหม่ในสคริปต์ บางสิ่งเช่นนี้

#!/bin/bash
VAR=rsync -avz --{partial,stats,delete,exclude=".*"}
$VAR /dir1 /dir2

ฉันได้ลองด้วยคำพูดคำพูดเดี่ยววงเล็บไม่ประสบความสำเร็จ


คำถามที่คล้ายกันเกี่ยวกับ SO: stackoverflow.com/questions/13365553/…
Barmar

ต้องลงเส้นทางนี้มาก่อน: ตรวจสอบให้แน่ใจว่าสตริงคำสั่งผลลัพธ์สุดท้ายของคุณไม่มีสตริงว่างใด ๆ ถ้าเป็นเช่นนั้น rsync อาจใช้เป็นพารามิเตอร์และเนื่องจากมองไม่เห็นมันจึงค่อนข้างยากที่จะทำการดีบัก ฉันมีพารามิเตอร์แรกที่ว่างเปล่าและ rsync ตีความว่ามันรวมถึงไดเรกทอรีปัจจุบันในแหล่งที่มาของสิ่งต่าง ๆ ที่จะคัดลอก
Joe

คำตอบ:


12

การวางคำสั่งที่ซับซ้อนในตัวแปรเป็นวิธีที่ไม่แนะนำ ดูBashFAQ / 050 - ฉันพยายามใส่คำสั่งในตัวแปร แต่กรณีที่ซับซ้อนมักจะล้มเหลว!

ความต้องการของคุณจะง่ายมาก ๆ ถ้าคุณแค่ตัดสินใจใช้ฟังก์ชั่นแทนตัวแปรและส่งผ่านข้อโต้แย้งไป

สิ่งที่ต้องการ

rsync_custom() {
    [ "$#" -eq 0 ] && { printf 'no arguments supplied' >&2; exit 1 ; }
    rsync -avz --{partial,stats,delete,exclude=".*"} "$@"
}

และตอนนี้ผ่านข้อโต้แย้งที่จำเป็นไปเป็น

rsync_custom /dir1 /dir2

นิยามฟังก์ชั่นนั้นค่อนข้างง่ายเราจะตรวจสอบจำนวนอาร์กิวเมนต์ที่ป้อนเข้าโดยใช้ตัวแปร$#ที่ไม่ควรเป็นศูนย์ เราส่งข้อความแสดงข้อผิดพลาดแจ้งว่าไม่มีข้อโต้แย้งใด ๆ หากมีข้อโต้แย้งที่ถูกต้องแล้ว"$@"หมายถึงข้อโต้แย้งที่เกิดขึ้นจริงให้กับฟังก์ชั่น

หากเป็นฟังก์ชั่นที่คุณจะใช้บ่อยคือสวยในสคริปต์ / บรรทัดคำสั่งนอกจากนี้ยังเพิ่มไปยังเปลือกเริ่มต้น-ไฟล์.bashrc, .bash_profileตัวอย่างเช่น

หรือตามที่ระบุไว้มันอาจคุ้มค่าที่จะขยายการขยายรั้งเพื่อแยกส่วนโค้งเพื่อให้อ่านง่ายขึ้นเช่น

rsync_custom() {
    [ "$#" -eq 0 ] && { printf 'no arguments supplied' >&2; exit 1 ; }
    rsync -avz --partial --stats --delete --exclude=".*" "$@"
}

5
VAR=rsync -avz --{partial,stats,delete,exclude=".*"}

พยายามนี้เพื่อเรียกใช้คำสั่ง-avzที่มีการขัดแย้ง--partial, --statsฯลฯ .. และมีการVARกำหนดให้rsyncอยู่ในสภาพแวดล้อม

VAR='rsync -avz --{partial,stats,delete,exclude=".*"}'

แบบฟอร์มที่ยกมาใช้ไม่ได้เพราะวงเล็บปีกกาไม่ได้ถูกขยายในเครื่องหมายคำพูดและไม่อยู่ในการรวมและไม่ถูกขยายหลังจากตัวแปรถูกขยาย

หากคุณต้องการเก็บอาร์กิวเมนต์บรรทัดคำสั่งในตัวแปรใช้อาร์เรย์:

args=(rsync -avz --{partial,stats,delete,exclude=".*"})

ตอนนี้"${args[@]}"จะขยายไปยังrsync, -avz, --partialฯลฯ เป็นคำที่แตกต่างกัน

อาร์เรย์ยังช่วยให้คุณสามารถผนวกตัวเลือกในรายการโดยมีเงื่อนไขหากจำเป็นดังนั้นคุณจึงสามารถ:

args=(this that)
if something ; then
    args+=(another_arg)
fi
"$cmd" "${args[@]}"

1

อย่างน้อยคุณสามารถบันทึกตัวเลือกบางส่วนในตัวแปร:

opts=$(echo --{ignore-case,word-regexp,count,exclude='"sys*.*"'})

การทดสอบมีความสำคัญเนื่องจากการปิดบังอาจทำได้ยาก:

echo $opts
--ignore-case --word-regexp --count --exclude="sys*"

grep $opts bytes *.log 

เนื่องจากมีหลายทางเลือกเช่นการใช้ประวัติการใช้นามแฝงการใช้ฟังก์ชั่นจึงไม่มีกรณีการใช้งานที่ชัดเจนที่ฉันสามารถนึกได้ การใช้ตัวเลือกที่ซับซ้อนร่วมกันระหว่างโปรแกรมที่แตกต่างกันมีน้อยดังนั้นสำหรับโซลูชัน ad-hoc สำหรับเชลล์แบบโต้ตอบนามแฝงดูเหมือนจะเป็นวิธีที่ดีกว่า:

alias cgrep='grep --ignore-case --word-regexp --count --exclude="sys*"'
cgrep bytes *.log

ตัวอย่างของคุณ

VAR=rsync -avz --{partial,stats,delete,exclude=".*"}

ไม่สามารถใช้งานได้เนื่องจากการมอบหมายนั้นสิ้นสุดที่ช่องว่างแรก คุณต้องปิดบังช่องว่าง:

VAR='rsync -avz --{partial,stats,delete,exclude=".*"}'

สิ่งที่ค่อนข้างอันตรายสำหรับการทดสอบโดยมีตัวเลือก - ลบใช่ไหม? เนื่องจากตัวเลือกอาจมี "," อีกครั้งและเครื่องหมายคำพูดเดี่ยวการปิดบังจึงอาจทำได้ยากในไม่ช้า ฉันจะไปหานามแฝงหรือพึ่งพาประวัติ

นามแฝงสามารถเก็บไว้ในไฟล์ ~ / .bashrc เพื่อใช้อย่างต่อเนื่องในหลาย ๆ เซสชัน ฟังก์ชั่นสามารถเก็บไว้ใน bashrc ได้เช่นกัน แต่คุณต้องการเพียงแค่ถ้าคุณต้องการจัดการพารามิเตอร์ที่ส่งผ่านไปยังฟังก์ชั่นที่จะได้รับการประเมินในนั้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.