วนรอบตัวแปรเชลล์ที่คั่นด้วยเครื่องหมายจุลภาค


114

สมมติว่าฉันมีตัวแปรเปลือก Unix ดังต่อไปนี้

variable=abc,def,ghij

ฉันต้องการที่จะดึงค่าทั้งหมด ( abc, defและghij) โดยใช้สำหรับวงและผ่านแต่ละค่าลงในขั้นตอน

$variableสคริปต์ควรอนุญาตให้มีการแยกจำนวนข้อของค่าคั่นด้วยเครื่องหมายจุลภาคจาก


คุณต้องการทำอะไรโดยการทำซ้ำ
SMA

1
ฉันต้องการส่งแต่ละฟิลด์ (พูดว่า abc) เป็นอาร์กิวเมนต์ของโพรซีเดอร์
Ramanathan K

คำตอบ:


136

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

variable=abc,def,ghij
for i in $(echo $variable | sed "s/,/ /g")
do
    # call your procedure/other scripts here below
    echo "$i"
done

แทนที่จะecho "$i"เรียกด้านบนระหว่างdoและdoneภายใน for loop คุณสามารถเรียกใช้ขั้นตอนของคุณproc "$i"ได้


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


หวังว่านี่จะช่วยได้


9
วิธีนี้ใช้ไม่ได้หาก$variableมีช่องว่างเช่นvariable=a b,c,d=> พิมพ์ 4 บรรทัด (a | b | c | d) แทนที่จะเป็น 3 (ab | c | d)
Dan

เช่นเดียวกับการเพิ่มคำพูดใหม่เช่น ** "value ** คุณช่วยอัปเดตได้ไหมว่ามี regex ใดที่จะลบออก ..
gks

144

ไม่ยุ่งกับ IFS
ไม่เรียกคำสั่งภายนอก

variable=abc,def,ghij
for i in ${variable//,/ }
do
    # call your procedure/other scripts here below
    echo "$i"
done

การใช้การจัดการสตริง bash http://www.tldp.org/LDP/abs/html/string-manipulation.html


4
ข้อดีของเรื่องนี้คือคุณสามารถซ้อนลูปหลาย ๆ อันโดยใช้ตัวคั่นต่าง ข้อเสนอแนะที่ดี!
Brad Parks

6
เคล็ดลับดีๆในการหลีกเลี่ยงความยุ่งเหยิงIFS!
Laimoncijus

15
คำเตือนเล็ก ๆ - หากค่าใด ๆ ใน$iมีช่องว่างค่าเหล่านั้นจะถูกแบ่ง (และนั่นอาจเป็นสาเหตุที่ส่งผ่านแบบคั่นด้วยจุลภาคแทนที่จะคั่นด้วยช่องว่าง)
Toby Speight

4
หากฟังก์ชั่นก่อนหน้าเปลี่ยนการตั้งค่า IFS จะไม่ได้ผล ... เปลี่ยนเป็น:for i in ${variable//,/$IFS} do; echo "$i"; done
Joep

2
ฉันได้รับข้อความแสดงข้อผิดพลาด "การทดแทนที่ไม่ถูกต้อง" เมื่อฉันลองใช้วิธีนี้
Lost Crotchet

56

หากคุณตั้งค่าตัวคั่นฟิลด์อื่นคุณสามารถใช้forลูปได้โดยตรง:

IFS=","
for v in $variable
do
   # things with "$v" ...
done

คุณยังสามารถจัดเก็บค่าในอาร์เรย์แล้ววนซ้ำตามที่ระบุไว้ในฉันจะแยกสตริงบนตัวคั่นใน Bash ได้อย่างไร :

IFS=, read -ra values <<< "$variable"
for v in "${values[@]}"
do
   # things with "$v"
done

ทดสอบ

$ variable="abc,def,ghij"
$ IFS=","
$ for v in $variable
> do
> echo "var is $v"
> done
var is abc
var is def
var is ghij

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

ตัวอย่างแนวทางที่สอง:

$ IFS=, read -ra vals <<< "abc,def,ghij"
$ printf "%s\n" "${vals[@]}"
abc
def
ghij
$ for v in "${vals[@]}"; do echo "$v --"; done
abc --
def --
ghij --

โปรดดูstackoverflow.com/questions/918886/… :-) ขอให้ทุกคนโชคดี
กระสุน

อ๋อ! ฉันยังคิดเกี่ยวกับเรื่องนั้นเพียงอย่างเดียวที่ต้องทำตามขั้นตอน: a whileเพื่ออ่านในอาร์เรย์และอีกอันหนึ่งเพื่อวนดูผลลัพธ์
fedorqui 'SO หยุดทำร้าย'

3
ทำให้ฉันมีความสุขเมื่อมีคนรู้วิธีใช้เปลือกแทนที่จะรวมกลุ่มของ binutils
PeterT

คุณต้องตั้งค่าIFSกลับไปยังสิ่งที่เป็นมาก่อนหรือไม่?
pstanton

1
@fedorqui มันทำ! นั่นเป็นตัวแปรเดียวที่ฉันต้องการวนซ้ำและทำให้ไฟล์ yaml ของฉันอ่านง่ายขึ้น (แทนที่จะเป็นตัวแปรสภาพแวดล้อม LONG หนึ่งตัว แต่จะมีแถวหลายแถว)
GammaGames

6
#/bin/bash   
TESTSTR="abc,def,ghij"

for i in $(echo $TESTSTR | tr ',' '\n')
do
echo $i
done

ฉันชอบใช้ tr แทน sed เนื่องจาก sed มีปัญหากับตัวอักษรพิเศษเช่น \ r \ n ในบางกรณี

วิธีอื่นคือการตั้งค่า IFS เป็นตัวคั่นบางตัว


1

นี่คือโซลูชันทางเลือกที่ใช้ tr ที่ไม่ใช้ echo ซึ่งแสดงเป็นซับเดียว

for v in $(tr ',' '\n' <<< "$var") ; do something_with "$v" ; done

รู้สึกเป็นระเบียบมากขึ้นโดยไม่มีเสียงสะท้อน แต่นั่นเป็นเพียงความชอบส่วนตัวของฉัน


0

ลองอันนี้.

#/bin/bash   
testpid="abc,def,ghij" 
count=`echo $testpid | grep -o ',' | wc -l` # this is not a good way
count=`expr $count + 1` 
while [ $count -gt 0 ]  ; do
     echo $testpid | cut -d ',' -f $i
     count=`expr $count - 1 `
done

แต่ถ้าฉันไม่แน่ใจเกี่ยวกับจำนวนฟิลด์ในตัวแปร testpid?
Ramanathan K

นอกจากนี้ฉันต้องส่งต่อตัวแปรข้างต้นแต่ละตัวเป็นอาร์กิวเมนต์ไปยังโพรซีเดอร์ และฉันต้องการทำสิ่งนี้จนกว่าความยาวของตัวแปรจะกลายเป็นศูนย์ (กล่าวคือฟิลด์ทั้งหมดควรถูกส่งผ่านไปยังขั้นตอนการประมวลผลหนึ่งครั้ง)
Ramanathan K

ฉันไม่รู้เกี่ยวกับขั้นตอน จากลิงค์นี้คุณจะได้รับจำนวนฟิลด์http://stackoverflow.com/questions/8629330/unix-count-of-columns-in-file. หลังจากนั้นให้ทำการวนซ้ำในขณะที่วนซ้ำ คุณจะได้รับสิ่งนั้น
Karthikeyan.RS

นี่คือการนับฟิลด์จากไฟล์ที่ฉันเดา จะเกิดอะไรขึ้นถ้าฉันต้องการนับฟิลด์ในตัวแปร (สมมติว่าตัวแปรคือ testpid = abc, def, ghij)
Ramanathan K

echo ตัวแปรและไพพ์เอาต์พุตไปยัง awk หรือดูคำตอบที่อัปเดตของฉัน อาจจะมีประโยชน์
Karthikeyan.RS

0

อีกวิธีหนึ่งที่ไม่ใช้ IFS และยังคงรักษาช่องว่างไว้:

$ var="a bc,def,ghij"
$ while read line; do echo line="$line"; done < <(echo "$var" | tr ',' '\n')
line=a bc
line=def
line=ghij

วิธีนี้ใช้งานได้อย่างถูกต้องใน bash แต่ zsh กลืนช่องว่าง
lleaff

0

นี่คือโซลูชัน bash ที่แท้จริงของฉันที่ไม่เปลี่ยน IFS และสามารถใช้ตัวคั่น regex ที่กำหนดเองได้

loop_custom_delimited() {
    local list=$1
    local delimiter=$2
    local item
    if [[ $delimiter != ' ' ]]; then
        list=$(echo $list | sed 's/ /'`echo -e "\010"`'/g' | sed -E "s/$delimiter/ /g")
    fi
    for item in $list; do
        item=$(echo $item | sed 's/'`echo -e "\010"`'/ /g')
        echo "$item"
    done
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.