ฉันจะขยายตัวแปรที่ยกมาเป็นอะไรถ้ามันว่างเปล่า?


21

ว่าฉันมีสคริปต์ทำ:

some-command "$var1" "$var2" ...

และในกรณีที่var1ว่างเปล่าฉันอยากจะแทนที่ด้วยไม่มีอะไรแทนที่สตริงว่างดังนั้นคำสั่งที่ดำเนินการคือ:

some-command "$var2" ...

และไม่:

some-command '' "$var2" ...

มีวิธีที่ง่ายกว่าการทดสอบตัวแปรและเงื่อนไขรวมถึงมันได้หรือไม่

if [ -n "$1" ]; then
    some-command "$var1" "$var2" ...
    # or some variant using arrays to build the command
    # args+=("$var1")
else
    some-command "$var2" ...
fi

มีการทดแทนพารามิเตอร์มากกว่าที่สามารถขยายไปเป็นอะไรใน bash, zsh หรือไม่ชอบ? ฉันอาจยังต้องการใช้ globbing ในส่วนที่เหลือของข้อโต้แย้งดังนั้นการปิดการใช้งานและการเลิกตัวแปรไม่ได้เป็นตัวเลือก


ฉันรู้ว่าฉันเคยเห็นและอาจเคยใช้สิ่งนี้มาก่อน แต่มันก็พิสูจน์ได้ยากในการค้นหา ตอนนี้ไมเคิลแสดงไวยากรณ์ฉันจำได้ว่าครั้งแรกที่ฉันเห็นมันเร็วพอ: unix.stackexchange.com/a/269549/70524 , unix.stackexchange.com/q/68484/70524
muru

หากคุณรู้ว่าเป็นการทดแทนพารามิเตอร์บางประเภททำไมคุณไม่ดูในส่วนการขยายพารามิเตอร์ของmanหน้า (-;
Philippos

1
@ ฟิลิปโปฉันไม่รู้ว่ามันเป็นอะไรในเวลานั้นเท่านั้นที่ฉันเคยเห็นหรือใช้งานมาก่อน รู้จักกันเป็นที่รู้จักและไม่รู้จักรู้จักกัน :(
muru

1
คะแนนคุกกี้พิเศษสำหรับการกล่าวถึงการใช้อาร์เรย์เพื่อเก็บอาร์กิวเมนต์ในคำถามนั้น
ilkkachu

คำตอบ:


29

เชลล์และBash ที่ สอดคล้องกับ Posix มี${parameter:+word} :

หากพารามิเตอร์ไม่ได้ตั้งค่าหรือเป็นโมฆะ null จะถูกแทนที่; มิฉะนั้นการขยายตัวของคำ (หรือสตริงที่ว่างถ้าคำถูกละเว้น) จะถูกแทนที่

ดังนั้นคุณสามารถทำได้:

${var1:+"$var1"}

และได้var1รับการตรวจสอบและ"$var1"นำไปใช้หากมีการตั้งค่าและไม่ว่างเปล่า (พร้อมกับกฎการอ้างอิงคู่แบบปกติ) มิฉะนั้นมันจะขยายออกไปเป็นอะไร โปรดทราบว่าเฉพาะส่วนภายในเท่านั้นที่ยกมาที่นี่ไม่ใช่ทุกสิ่ง

เดียวกันยังทำงานใน zsh คุณต้องทำซ้ำตัวแปรดังนั้นจึงไม่เหมาะ แต่จะได้ผลตามที่คุณต้องการ

หากคุณต้องการให้ตัวแปร set-but-empty ขยายเป็นอาร์กิวเมนต์ว่างให้ใช้${var1+"$var1"}แทน


1
ดีพอสำหรับฉัน ดังนั้นในสิ่งนี้สิ่งทั้งหมดไม่ได้ยกมามีเพียงwordบางส่วนเท่านั้น
muru

1
เนื่องจากคำถามที่ถามถึง "bash, zsh หรือสิ่งที่คล้ายกัน" (และสำหรับการเก็บถาวรถาม & ตอบ) ฉันแก้ไขเพื่อให้เห็นว่านี่เป็นคุณลักษณะ posix แม้ว่าคุณจะเพิ่มแท็ก / bash ในคำถามหลังจากความคิดเห็นของฉัน (-;
Philippos

2
ฉันเอาเสรีภาพของการแก้ไขในความแตกต่างระหว่างและ:+ +
ilkkachu

ขอบคุณมากสำหรับโซลูชันที่สอดคล้องกับ POSIX! ฉันพยายามใช้sh/ dashสำหรับสคริปต์ chokepoint ที่อาจเกิดขึ้นดังนั้นฉันมักจะขอบคุณมันเมื่อมีคนแสดงให้เห็นว่าสิ่งที่เป็นไปได้โดยไม่ต้องหันไปใช้ Bash
JamesTheAwesomeDude

ฉันกำลังมองหาผลตรงกันข้าม (ขยายเป็นอย่างอื่นถ้ามันเริ่มเป็นโมฆะ) ปรากฎว่าตรรกะนี้สามารถคว่ำโดยการเปลี่ยน + เป็น - ใน: $ {empty_var: -replacement}
Alex Jansen

5

zshโดยค่าเริ่มต้นสิ่งนี้จะเกิดขึ้นเมื่อคุณไม่ใส่เครื่องหมายคำพูด:

some-command $var1 $var2

ที่จริงแล้วเหตุผลเดียวที่คุณยังต้องมีเครื่องหมายอัญประกาศใน zsh รอบการขยายพารามิเตอร์คือเพื่อหลีกเลี่ยงพฤติกรรมนั้น (การลบที่ว่างเปล่า) เนื่องจากzshไม่มีปัญหาอื่น ๆ ที่ส่งผลกระทบต่อเชลล์อื่น ๆ เมื่อคุณไม่ได้อ้างการขยายพารามิเตอร์ glob)

คุณสามารถทำเช่นเดียวกันกับกระสุนคล้าย POSIX อื่น ๆ หากคุณปิดการใช้งาน split และ glob:

(IFS=; set -o noglob; some-command $var1 $var2)

ตอนนี้ฉันจะยืนยันว่าหากตัวแปรของคุณมีค่า 0 หรือ 1 ก็ควรเป็นอาร์เรย์และไม่ใช่ตัวแปรสเกลาร์และการใช้งาน:

some-command "${var1[@]}" "${var2[@]}"

และใช้var1=(value)เมื่อvar1ใดที่จะมีค่าหนึ่งค่าvar1=('')เมื่อมันมีค่าหนึ่งค่าที่ว่างเปล่าและvar1=()เมื่อค่านั้นไม่มีค่า


0

ฉันวิ่งเข้าไปในนี้โดยใช้ rsync ในสคริปต์ทุบตีที่เริ่มต้นคำสั่งโดยมีหรือไม่มีการ-nสลับการทำงานแบบแห้ง ปรากฎว่า rsync และคำสั่ง gnu จำนวนหนึ่งใช้''เป็นอาร์กิวเมนต์แรกที่ถูกต้องและทำหน้าที่แตกต่างจากที่ไม่มีอยู่

การดำเนินการนี้ค่อนข้างใช้เวลาสักครู่ในการดีบักเนื่องจากพารามิเตอร์ null เกือบมองไม่เห็นทั้งหมด

บางคนในรายการ rsync แสดงให้ฉันเห็นวิธีหลีกเลี่ยงปัญหานี้ในขณะที่ยังทำให้การเข้ารหัสของฉันง่ายขึ้นมาก หากฉันเข้าใจถูกต้องนี่เป็นความแตกต่างของคำแนะนำสุดท้ายของ @ Stéphane Chazelas

สร้างอาร์กิวเมนต์คำสั่งของคุณด้วยตัวแปรแยกต่างหากจำนวนหนึ่ง สามารถตั้งค่าเหล่านี้ในลำดับหรือตรรกะที่เหมาะสมกับปัญหา

จากนั้นในตอนท้ายใช้ตัวแปรเพื่อสร้างอาร์เรย์ที่มีทุกอย่างในตำแหน่งที่เหมาะสมและใช้เป็นอาร์กิวเมนต์สำหรับคำสั่งจริง

วิธีนี้คำสั่งถูกเรียกใช้ในที่เดียวในโค้ดแทนการทำซ้ำสำหรับแต่ละรูปแบบของอาร์กิวเมนต์

ตัวแปรที่ว่างเปล่าเพียงหายไปโดยใช้วิธีนี้

ฉันรู้ว่าการใช้ eval นั้นขมวดคิ้วอย่างมาก ฉันจำรายละเอียดทั้งหมดไม่ได้ แต่ดูเหมือนว่าฉันจะต้องใช้มันเพื่อให้สิ่งต่าง ๆ ทำงานได้ด้วยวิธีนี้ - บางอย่างเกี่ยวกับการจัดการพารามิเตอร์ด้วย white space

ตัวอย่าง:

dry_run=''
if [[ it is a test run ]]
then
  dry_run='-n'
fi
...
rsync_options=(
  ${dry_run}
  -avushi
  ${delete}
  ${excludes}
  --stats
  --progress
)
...
eval rsync "${rsync_options[@]}" ...

นั่นเป็นวิธีที่แย่กว่าในการทำสิ่งที่ฉันอธิบายไว้ในคำถาม (การสร้างข้อโต้แย้งมากมายตามเงื่อนไข) ใครจะรู้ว่าคุณกำลังทิ้งปัญหาตัวเองไว้
muru

@muru คุณพูดถูก แต่ฉันก็ยังต้องการสิ่งนี้อยู่ ฉันไม่ค่อยเห็นวิธีการแก้ไขโดยใช้เทคนิคจากคำตอบอื่น ๆ มันจะเป็นการดีถ้าได้เห็นชิ้นส่วนของโค้ดทำในสิ่งที่ถูกต้อง ฉันจะเล่นกับตัวเลือกสุดท้ายของ Stephane ในภายหลังเนื่องจากอาจทำสิ่งที่ฉันต้องการ
Joe

ชอบpaste.ubuntu.com/26383847หรือ
muru

เพิ่งกลับมาที่นี่ ขอบคุณสำหรับตัวอย่าง มันทำให้รู้สึก ฉันจะทำงานกับมัน
Joe

ฉันมีกรณีการใช้งานที่คล้ายกันที่นี่ แต่คุณสามารถทำสิ่งนี้: ถ้า ["$ dry" == "true"]; จากนั้น dry = "- n"; fi ... ดังนั้นหากตัวแปร dry เป็นจริงคุณทำให้มันเป็น -n และจากนั้นคุณสร้างคำสั่ง rsync ตามปกติและมีดังนี้: rsync $ {dry1: + "$ dry1"} ... ถ้ามันเป็นโมฆะ ไม่มีอะไรจะเกิดขึ้นไม่เช่นนั้นมันจะกลายเป็น -n ตามคำสั่งของคุณ
Freedo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.