วิธีการแยกสตริงที่คั่นด้วยเป็นอาร์เรย์ใน awk?


169

วิธีการแยกสตริงเมื่อประกอบด้วยสัญลักษณ์ไปป์|ในนั้น ฉันต้องการแยกพวกมันออกเป็นอาเรย์

ฉันเหนื่อย

echo "12:23:11" | awk '{split($0,a,":"); print a[3] a[2] a[1]}'

ซึ่งใช้งานได้ดี หากสตริงของฉันเป็นเช่น"12|23|11"นั้นฉันจะแบ่งออกเป็นอาร์เรย์ได้อย่างไร


3
โปรดทราบว่าผลลัพธ์ของคุณเชื่อมต่อองค์ประกอบของอาร์เรย์โดยไม่มีตัวคั่น หากคุณต้องการให้พวกเขาแยกออกจากกันOFSให้ใช้เครื่องหมายจุลภาคคั่นระหว่างพวกเขาทำให้printเห็นว่าพวกเขาเป็นอาร์กิวเมนต์แยกต่างหาก
dubiousjim

หรือคุณสามารถใช้ sed:echo "12:23:11" | sed "s/.*://"
เฉอะแฉะ

@slushy: คำสั่งของคุณไม่ได้อยู่ที่ความต้องการของผู้ถาม คำสั่งของคุณ ( echo "12:23:11" | sed "s/.*://") ลบทุกอย่างจนกระทั่ง (และรวมถึง) ตัวสุดท้าย ":" โดยเก็บเฉพาะ "11" ... มันจะทำงานเพื่อให้ได้หมายเลขสุดท้าย แต่จะต้องมีการแก้ไข (ในทางที่อ่านยาก) เพื่อให้ได้ หมายเลขที่สอง ฯลฯ awk (และการแยก awk) มีความสวยงามและอ่านได้มากกว่า
Olivier Dulac

หากคุณต้องการแยกตัวละครเดี่ยวคุณสามารถใช้cut
ccpizza

คำตอบ:


274

คุณเคยลองไหม:

echo "12|23|11" | awk '{split($0,a,"|"); print a[3],a[2],a[1]}'

2
@Mohamed Saligh หากคุณใช้ระบบ Solaris คุณต้องใช้/ usr / xpg4 / bin / awkตามความยาวสตริง
Dimitre Radoulov

5
'ไม่ทำงานสำหรับฉัน' โดยเฉพาะอย่างยิ่งกับเครื่องหมายทวิภาคระหว่างค่าเสียงสะท้อนและการแยกตั้งค่าเพื่อแยกบน '|' ??? สะกดผิด? โชคดีทุกคน.
shellter

1
ดีกว่าด้วยคำอธิบายทางไวยากรณ์
Alston

2
สิ่งนี้จะไม่ทำงานใน GNU awk เนื่องจากอาร์กิวเมนต์ที่สามsplitเป็นนิพจน์ทั่วไปและ|เป็นสัญลักษณ์พิเศษที่ต้องหลบหนี ใช้split($0, a, "\|")
WhiteWind

1
@ WhiteWind: อีกวิธีในการ "มั่นใจ" ที่|ถูกมองว่าเป็น char และไม่ใช่สัญลักษณ์พิเศษที่จะใส่ระหว่าง[]: เช่น split($0, a, "[|]") # ฉันชอบสิ่งนี้ดีกว่า '\ |' ในบางกรณีโดยเฉพาะอย่างยิ่งบาง regexp ( perl vs grep vs .. others?) สามารถมี "|" แทรกตัวอักษรและ "\ |" ถูกมองว่าเป็นตัวคั่น regex แทนที่จะตรงข้าม ... ymmv
Olivier Dulac

119

ในการแยกสตริงเป็นอาร์เรย์ในawkเราใช้ฟังก์ชันsplit():

 awk '{split($0, a, ":")}'
 #           ^^  ^  ^^^
 #            |  |   |
 #       string  |   delimiter
 #               |
 #               array to store the pieces

หากไม่ได้รับตัวคั่นจะใช้การFSซึ่งเป็นค่าเริ่มต้นไปยังพื้นที่:

$ awk '{split($0, a); print a[2]}' <<< "a:b c:d e"
c:d

เราสามารถให้ตัวแยกตัวอย่างเช่น::

$ awk '{split($0, a, ":"); print a[2]}' <<< "a:b c:d e"
b c

ซึ่งเทียบเท่ากับการตั้งค่าผ่านFS:

$ awk -F: '{split($0, a); print a[1]}' <<< "a:b c:d e"
b c

ในเพ่งพิศคุณยังสามารถให้ตัวแยกเป็น regexp:

$ awk '{split($0, a, ":*"); print a[2]}' <<< "a:::b c::d e" #note multiple :
b c

และยังเห็นสิ่งที่ตัวคั่นอยู่บนทุกขั้นตอนโดยใช้พารามิเตอร์ตัวที่สี่:

$ awk '{split($0, a, ":*", sep); print a[2]; print sep[1]}' <<< "a:::b c::d e"
b c
:::

ลองอ้างหน้า man ของ GNU awk :

แยก (สตริง, อาร์เรย์ [, fieldep [, seps]])

แบ่งสตริงออกเป็นชิ้น ๆ โดยคั่นด้วยfieldepและเก็บชิ้นส่วนในอาร์เรย์และสตริงตัวคั่นในอาร์เรย์seps ชิ้นแรกจะถูกเก็บไว้ในarray[1]ชิ้นที่สองในarray[2]และอื่น ๆ ค่าสตริงของอาร์กิวเมนต์ที่สามคือfieldepเป็น regexp อธิบายตำแหน่งที่จะแยกสตริง (มากFSสามารถเป็น regexp อธิบายที่จะแยกบันทึกอินพุต) หากfieldsepถูกละไว้ค่าของFSถูกนำมาใช้ ส่งคืนจำนวนองค์ประกอบที่สร้างขึ้น sepsเป็นส่วนขยายโดยเป็นสตริงตัวคั่นระหว่างและ ถ้าfieldepเป็นช่องว่างเดียวช่องว่างนำหน้าใด ๆ จะเข้าสู่และช่องว่างต่อท้ายใด ๆ ก็ตามที่เข้ามาโดยที่nคือค่าส่งคืนของsplit()gawkseps[i]array[i]array[i+1]seps[0]seps[n]split() (เช่นจำนวนองค์ประกอบในอาร์เรย์)


เพียงแค่พูดถึงคุณกำลังใช้ GNU awk ไม่ awk ปกติ (ซึ่งไม่ได้จัดเก็บแยกในยินดี [] และมีข้อ จำกัด อื่น ๆ )
โอลิเวีย Dulac

17

โปรดเจาะจงมากขึ้น! คุณหมายถึงอะไรโดย "ไม่ทำงาน"? โพสต์ผลลัพธ์ที่แน่นอน (หรือข้อความแสดงข้อผิดพลาด), เวอร์ชั่น OS และ awk ของคุณ:

% awk -F\| '{
  for (i = 0; ++i <= NF;)
    print i, $i
  }' <<<'12|23|11'
1 12
2 23
3 11

หรือใช้การแบ่ง:

% awk '{
  n = split($0, t, "|")
  for (i = 0; ++i <= n;)
    print i, t[i]
  }' <<<'12|23|11'
1 12
2 23
3 11

แก้ไข: บนSolarisคุณจะต้องใช้POSIX awk ( / usr / xpg4 / bin / awk ) เพื่อประมวลผล 4000 ฟิลด์อย่างถูกต้อง


for(i = 0หรือfor(i = 1?
PiotrNycz

ฉัน = 0 เพราะฉันใช้ ++ ฉันหลังจาก (ไม่ใช่ฉัน ++)
Dimitre Radoulov

3
ตกลง - ฉันไม่ได้สังเกตเรื่องนี้ ฉันเชื่ออย่างยิ่งว่าจะสามารถอ่านได้มากขึ้นfor (i = 1; i <= n; ++i)...
PiotrNycz

5

ฉันไม่ชอบecho "..." | awk ...วิธีแก้ปัญหาเพราะเรียกว่าไม่จำเป็นforkและexecเรียกระบบ

ฉันชอบทางออกของ Dimitre ที่มีการบิดเล็กน้อย

awk -F\| '{print $3 $2 $1}' <<<'12|23|11'

หรือรุ่นที่สั้นกว่าเล็กน้อย:

awk -F\| '$0=$3 $2 $1' <<<'12|23|11'

ในกรณีนี้บันทึกผลลัพธ์จะรวมกันซึ่งเป็นเงื่อนไขที่แท้จริงดังนั้นจึงได้รับการพิมพ์

ในกรณีเฉพาะนี้การstdinเปลี่ยนเส้นทางสามารถไว้ชีวิตได้ด้วยการตั้งค่า ตัวแปรภายใน:

awk -v T='12|23|11' 'BEGIN{split(T,a,"|");print a[3] a[2] a[1]}'

ฉันใช้ ค่อนข้างนาน แต่ใน สิ่งนี้สามารถจัดการได้โดยการจัดการสตริงภายใน ในกรณีแรกสตริงเดิมจะถูกแบ่งด้วยตัวยุติภายใน ในกรณีที่สองมันจะสันนิษฐานว่าสตริงมีคู่หลักคั่นด้วยตัวคั่นตัวเดียวเสมอ

T='12|23|11';echo -n ${T##*|};T=${T%|*};echo ${T#*|}${T%|*}
T='12|23|11';echo ${T:6}${T:3:2}${T:0:2}

ผลลัพธ์ในทุกกรณีคือ

112312

ฉันคิดว่าผลลัพธ์สุดท้ายควรจะเป็นการอ้างอิงตัวแปรอาร์เรย์ awk โดยไม่คำนึงถึงตัวอย่างงานพิมพ์ที่ได้รับ แต่คุณพลาดกรณีทุบตีที่ง่ายมากที่จะให้ผลลัพธ์สุดท้ายของคุณ T = '12: 23: 11 '; echo $ {T //:}
Daniel Liston

@DanielListon คุณพูดถูก! ขอบคุณ! ฉันไม่ทราบว่าส่วนท้าย / สามารถทิ้งไว้ในbashนิพจน์นี้...
TrueY

4

อันที่จริงawkมีคุณสมบัติที่เรียกว่า 'ฟิลด์ป้อนข้อมูลการแยกตัวแปร' การเชื่อมโยง นี่คือวิธีการใช้งาน มันไม่ได้เป็นอาร์เรย์จริงๆ แต่ใช้ตัวแปร $ ภายใน สำหรับการแยกสตริงง่าย ๆ มันง่ายกว่า

echo "12|23|11" | awk 'BEGIN {FS="|";} { print $1, $2, $3 }'



1

เรื่องตลก? :)

เกี่ยวกับ echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

นี่คือผลลัพธ์ของฉัน:

p2> echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'
112312

ดังนั้นฉันคิดว่ามันจะทำงานหลังจากทั้งหมด ..


นั่นเป็นเพราะความยาวของสตริงหรือไม่ ตั้งแต่ความยาวสตริงของฉันคือ 4,000 ความคิดใด ๆ
Mohamed Saligh

1

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

# Convert to an array
_ITEMS=($(echo "12|23|11" | tr '|' '\n'))

# Output array items
for _ITEM in "${_ITEMS[@]}"; do
  echo "Item: ${_ITEM}"
done

ผลลัพธ์จะเป็น:

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