ฉันสามารถใช้ตัวแปรในการขยาย Bash brace ได้หรือไม่?


10

ด้านล่างนี้เป็นโค้ดหลอกบางประเภทสำหรับสิ่งที่ฉันพยายามทำ:

#!/bin/bash

# I already have the variable below figured out (positive integer):
numlines=$([returns number of lines containing specific characters in a file])

# This is basically what I want to do with it:
for i in {1..$numlines}; do
    # the part below is already figured out as well:        
    do some other stuff
done

ฉันสามารถดำเนินการได้ดีจากบรรทัดคำสั่งโดยการใส่หมายเลขจริงในลำดับ `{1..n} ' ฉันแค่ต้องรู้ว่าเป็นไปได้หรือไม่ที่จะรวมตัวแปรไว้ที่นี่และจะทำอย่างไรกับมัน

  • ฉันได้ลองexportมัน
  • ฉันได้ลองใส่ตัวแปรในวงเล็บปีกกาภายในลำดับ: {1..${numlines}}
  • ฉันได้ลองใส่ไว้ในเครื่องหมายคำพูดคู่หวังว่ามันจะขยาย: {1.."$numlines"}
  • ฉันพยายามหลบหนี$:{1..\$numlines}

ฉันจำเป็นต้องใช้set -[something]คำสั่งเพื่อให้ตัวแปรนี้ถูกขยายหรือไม่ ฉันได้ลองใช้รูปแบบeval... ทั้งหมดเพื่อประโยชน์

ฉันแค่ต้องรู้ว่ามีบางสิ่งที่เรียบง่ายหรือคลุมเครือที่ฉันขาดหายไปหรือหากเป็นไปได้ก่อนที่ฉันจะเสียเวลาอีกต่อไป

ฉันสามารถรวมกันเป็นแฮ็คจริง ๆของวิธีการทำเพื่อให้ทำงานตามที่ต้องการ แต่ฉันต้องการหลีกเลี่ยงที่ถ้าเป็นไปได้ทั้งหมดและเรียนรู้วิธีการด้านขวาไปเกี่ยวกับการทำ


คำตอบ:


11

น่าเสียดายที่ไม่มีวิธีการใช้ตัวแปรในการขยายตัว (AFAIK) เนื่องจากการขยายตัวของตัวแปรเกิดขึ้นหลังจากการขยายรั้ง

โชคดีที่มีเครื่องมือที่ทำงานเหมือนกัน

for i in $(seq 1 $numlines); do
    # stuff
done

seqมาจาก GNU coreutils; ไม่รู้จะทำอย่างไรใน POSIX


1
(บวก 1) seqดีสำหรับระบบ GNU และหากฉันจำได้ถูกต้อง OSX ล่าสุด ในระบบ BSD อื่น ๆ สามารถใช้jotแทนได้
John1024

seqทำงานได้อย่างสมบูรณ์ ขอบคุณมากสำหรับคำตอบที่รวดเร็ว
rubynorails

นี่ไม่ใช่ส่วนหนึ่งของคำถามของฉัน - แต่อะไรคือไวยากรณ์ (ถ้ามี) สำหรับการทำสิ่งนี้ในลำดับย้อนกลับเช่น{16..1}? $(seq $numlines 1)ไม่ทำงาน. ฉันเดาว่าฉันสามารถทำได้เสมอman seqแต่แค่สงสัยว่ามีใครรู้จากส่วนหัวของพวกเขาหรือไม่
rubynorails

1
เพียงแค่คิดออกว่าจะทำในสิ่งที่ตรงกันข้ามจากลิงค์นี้ -for i in $(seq $numlines -1 1)
rubynorails

seq ${numlines} -1 0
DopeGhoti

10

แน่ใจ หากคุณต้องการการวนรอบที่เพิ่มตัวแปรจำนวนเต็มใช้รูปแบบของการforวนรอบที่เพิ่มตัวแปรจำนวนเต็ม (หรือโดยทั่วไปดำเนินการทางคณิตศาสตร์ในตัวแปรห่วง

for ((i=1; i<=numlines; i++)); do  done

โครงสร้างนี้ทำงานใน bash (และ ksh93 และ zsh) แต่ไม่อยู่ใน sh แบบธรรมดา ใน sh แบบธรรมดาให้ใช้ a while loop และการทดสอบ ( [ … ])

i=1
while [ "$i" -le "$numlines" ]; do
  
  i=$((i+1))
done

8

หากคุณต้องหลีกเลี่ยงseqซึ่งเป็น Tom Hunt ชี้ให้เห็นว่าเป็นวิธีปกติในการแก้ปัญหานี้แล้วevalแน่นอนสามารถทำได้ (แม้ว่าฉันจะไม่สนับสนุนให้)

eval 'for i in {1..'$numlines'}; do echo $i; done'

คุณสามารถอยู่ POSIX ได้โดยหลีกเลี่ยงการขยาย {} และทำการเปรียบเทียบทางคณิตศาสตร์และจำนวนเต็มใน$numlines:

while [ ! "$numlines" -eq 0 ]; do
     echo "$numlines"
     : $((numlines-=1))
done

ด้านนอกของ POSIX bashและkshและzshยังมีforลูปแบบ C :

for((i=0; i<numlines; i++)); do echo $i; done

1
ฉันขอบคุณคำตอบนี้เช่นกัน ในขณะที่seqทำงานได้ดีสำหรับสถานการณ์ของฉันและดูเหมือนจะเป็นวิธีที่ง่ายที่สุดก็ดีที่จะรู้ว่ามีทางเลือกอื่น ๆ (แม้ POSIX) ขอบคุณสำหรับสิ่งนี้.
rubynorails

2
ไม่มีเหตุผลจริงๆที่จะใช้eval; หากคุณมีการขยายรั้งคุณมีห่วงสไตล์ C
chepner

@PSkocik - หากฉันเลือกได้ 2 คำตอบฉันก็จะเลือกคำตอบนี้ด้วย เมื่อฉันมาข้ามความจริงที่ว่าฉันต้องการที่จะทำเช่นนี้ในการย้อนกลับของคุณตัวอย่างเช่นเป็นที่ง่ายและจะมีการบันทึกฉันจากที่มีการค้นหาวิธีการอื่นในการดำเนินการได้โดยใช้eval ห่วงเป็นใหญ่บิตสำหรับฉัน ฉันชอบที่จะทำให้สิ่งต่าง ๆ สั้นและหวานใจและฉันก็ไม่สามารถทำให้วง C-style ทำงานได้โดยให้ค่าเป็น 0 หรือ 1 มันไม่เคยกลับมาอย่างถูกต้องอีกต่อไป ฉันแน่ใจว่ามันอาจถูก tweaked ในการทำงานอย่างถูกต้อง แต่สิ่งเหล่านี้เป็นโซลูชั่นที่มีประโยชน์แน่นอนอย่างไรก็ตาม seqwhilefori
rubynorails

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