การจัดเรียงคอลัมน์ใหม่โดยใช้ awk


13

ฉันพยายามย้ายคอลัมน์ที่ 7 ของไฟล์ csv ไปยังจุดสิ้นสุดโดยใช้

awk -F '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}',OFS= "$file"

โดยที่ $ file เป็นไฟล์. csv ในไดเรกทอรี อย่างไรก็ตามผลลัพธ์คือ

awk:                          ^ syntax error

ไม่มีใครรู้วิธีแก้ไขข้อผิดพลาดนี้หรือไม่?


7
เมื่อแสดงข้อผิดพลาด awk คุณจะต้องแสดงทุกสิ่ง ^บ่งชี้เฉพาะส่วนของคำสั่งข้อผิดพลาดที่พบ
terdon

คำตอบ:


11

-Fตัวเลือกที่ต้องการอาร์กิวเมนต์: -F,ตัวอย่างเช่น

จุดสิ้นสุดของawkสคริปต์ต้องคั่นด้วย(space char) กับพารามิเตอร์ที่เหลือ

หากตัวคั่นฟิลด์เป็น,และคุณต้องการเก็บไว้และถ้าจำนวนคอลัมน์คงที่และต่ำกว่าหรือเท่ากับ 11 ลองทำสิ่งนี้:

awk -F, '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}' OFS=, "$file"

8
@anuribs มีโปรแกรมน้อยมากที่อนุญาต command file > newfile && mv newfile fileวิธีมาตรฐานคือ ที่กล่าวว่ารุ่นใหม่ของ GNU ให้การสนับสนุนนี้:awk gawk -i inplace '{blah blah}' file
terdon

1
อีกทางเลือกหนึ่งแทนการmv newfile fileที่คุณสามารถใช้cat newfile > file ; rm -f newfile- นี้เก็บรักษา inode fileและสิทธิ์ของ
cas

และโดยทั่วไปแล้วควรใช้mktempชื่อแฟ้มชั่วคราวที่เข้ารหัสยากลงในสคริปต์ เช่นtf=$(mktemp) ; command file > "$tf" ; cat "$tf" > file ; rm -f "$tf"
cas

8

วิธีแก้ปัญหาที่สั้นกว่าก็คือ

awk -F',+' -v OFS=, '{$(NF+1)=$7; $7=""; $0=$0; $1=$1}1' file

ฉันไม่แน่ใจว่า,+จะใช้ได้กับทุกawkรุ่นหรือไม่ แต่ทำงานอย่างน้อยใน GNU awk ด้วย-cโหมด ompatibility

คำอธิบาย:

  • $(NF+1)=$7: ครั้งแรกที่เราเพิ่มเขตข้อมูลที่ 7 ถึงจุดสิ้นสุดของบรรทัด (อาจเป็น$12=$7ในกรณีนี้)
  • $7="": ในฟิลด์ขั้นตอนที่ 7 ถัดไปจะถูกลบ (แต่ตัวคั่นที่ล้อมรอบอยู่)
  • ในการลบตัวคั่นเราจำเป็นต้องตั้งค่าเรคคอร์ดทั้งหมดอีกครั้ง (ผ่าน$0=$0) การใช้เครื่องหมายจุลภาคหลายตัวเป็นตัวคั่นฟิลด์ (นี่คือทำผ่าน-F',+'นี่+หมายถึงหนึ่งครั้งหรือมากกว่า) และจัดเรียงเรคคอร์ดปัจจุบันผ่านทาง$1=$1เพื่อบังคับให้สร้างบรรทัดใหม่ ตัวคั่น (ตั้งค่าโดยตัวเลือก-v OFS=,)
  • หลังจากสับเสร็จแล้วเราก็พร้อมที่จะพิมพ์ผลลัพธ์ด้วย 1

อินพุตตัวอย่าง:

1,2,3,4,5,6,7,8,9,10,11

เอาท์พุต

1,2,3,4,5,6,8,9,10,11,7

เกิดอะไรขึ้นถ้าคอลัมน์อื่นว่างเปล่า แต่ใช่ FS เป็นนิพจน์ปกติใน POSIX (ถ้าเป็นอักขระหลายตัว) ดังนั้น,+ควรใช้งานได้
Random832

(1) ฉันเข้าใจว่าการทำให้คอลัมน์ที่เจ็ดของข้อมูลอินพุต“ หายไป” และไม่เพียง แต่ตั้งค่าเป็นโมฆะเป็นส่วนที่ยุ่งยากของปัญหานี้ แต่อย่างที่ Random832 พูดว่าวิธีแก้ปัญหาของคุณนั้นจะทำให้คอลัมน์ว่าง (ตัวอย่างเช่นall,ball,call,,,fallall,ball,call,fall) (2)  $(NF+1)=$7เป็นวิธีที่ฉลาด IMHO $0 = $0 OFS $7นั้นชัดเจนขึ้นเพียงไม่กี่ตัวอักษรและดูเหมือนว่าจะทำในสิ่งเดียวกัน คุณนึกถึงสถานการณ์ที่$0 = $0 OFS $7ไม่ทำเช่นเดียวกับรหัสของคุณหรือไม่?
G-Man กล่าวว่า 'Reinstate Monica'

@ Random832 @ G-Man ใช่กรณีขอบบางอย่างเช่นฟิลด์ว่าง, บรรทัดว่างหรือ NF <7 ควรได้รับการปฏิบัติแยกต่างหากหรืออย่างใดอย่างหนึ่งควรจัดเรียงรหัสใหม่ นี่เป็นเพียงความคิดไม่ใช่ "วิธีแก้ปัญหาที่สมบูรณ์" สำหรับกรณีทั่วไปทั้งหมดที่ควรชัดเจน $0=$0 OFS $7อาจเหมือนกัน$(NF+1)=$7แต่มีเพียงส่วนที่เหลือของรหัสไม่เปลี่ยนแปลงไม่ใช่โดยทั่วไป
jimmij

5

หากคุณกำลังพิมพ์ด้วยOFS=ดังนั้นหากไม่มีตัวคั่นระหว่างฟิลด์คุณสามารถบันทึกค่าของ$7ตัวแปรตั้งค่า$7ให้ว่างและพิมพ์บรรทัดและตัวแปรโดยตรง คุณไม่จำเป็นต้องระบุฟิลด์ทั้งหมด:

$ cat file
1,2,3,4,5,6,7,8
$ awk -F, -vOFS= '{k=$7; $7=""; print $0,k}' file 
12345687

3

คุณอาจหมายถึง:

awk -F, -v OFS='' '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}' "$file"

คุณรู้ไหมว่าawkไม่เคยเห็นเครื่องหมายคำพูดเดียวOFS=''ใช่มั้ย คุณอาจพิมพ์เพียงแค่OFS=; มันเหมือนกันหมด
Wildcard

1
ใช่ฉันรู้แล้ว อย่างไรก็ตามฉันไม่ชอบงานมอบหมาย
Michael Vehrs


3

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

MOVECOL=7
N=$((MOVECOL-1))
sed -r -e "s/^(([^,]*,){$N})([^,]*),(.*)/\1\4,\3/" -i test.csv

คำอธิบาย:

  • -r เลือก regexps แบบขยายดังนั้นเราจึงหลีกเลี่ยงแบ็กสแลชจำนวนมาก
  • กลุ่มแรกคือการซ้ำ $ N ของสตริงที่สิ้นสุดด้วยเครื่องหมายจุลภาคหรืออีกนัยหนึ่งคือคอลัมน์ที่อยู่ก่อนหน้าคอลัมน์ที่เราต้องการย้ายด้วยเครื่องหมายจุลภาคสุดท้าย
  • กลุ่มที่สองคือการทำซ้ำ $ N-th เราลืมมัน
  • กลุ่มที่สามคือคอลัมน์ที่เราต้องการย้ายโดยไม่มีเครื่องหมายจุลภาคสุดท้าย
  • กลุ่มที่สี่ประกอบด้วยคอลัมน์ทั้งหมดหลังจากกลุ่มที่เราต้องการย้ายโดยไม่มีเครื่องหมายจุลภาคมาก่อน
  • เราแทนที่ด้วยกลุ่มแรกกลุ่มสุดท้ายและคอลัมน์ที่เราคลายบีบอัดใส่เครื่องหมายจุลภาคตามต้องการ

แน่นอนว่าสิ่งนี้จะไม่ทำงานกับไฟล์ที่ซ่อนเครื่องหมายจุลภาคในเครื่องหมายคำพูด (หรือแย่กว่านั้นคือยกเว้นพวกเขา) แต่ awk จะไม่จัดการกับสิ่งนั้นโดยไม่มีการแสดงผาดโผนร้ายแรง ถ้าคุณมีปัญหาที่คุณต้องการจะดีกว่ากับperlโมดูลText:CSVหรือโมดูลpythoncsv


2

awkตัวแปรสองตัว(สมมติว่าไฟล์ของคุณอยู่ในตัวแปร$file)

  • ที่นี่คุณสามารถวนรอบ coloumn ทั้งหมดพิมพ์ด้วยตัวคั่นฟิลด์ (OFS) และพิมพ์ record terminator (ORS) ที่ท้ายบรรทัด

    awk  -F',' -v OFS=,                                \
    '{for(i=1;i<=NF;i++) if (i!=7) printf "%s",$i OFS; \
    printf "%s",$7;printf ORS}' "$file"
  • ที่นี่ด้วยการใช้ regex และgensub()ฟังก์ชั่น

    gawk -F',+' -v OFS=, '{$0=gensub(/\s*\S+/,"",7) OFS $7}1' "$file"

    ฆ่า 7 THข้อมูลและการพิมพ์ในตอนท้ายของบรรทัด

    • $0 เป็นบันทึกทั้งหมด
    • $nเป็น n THบันทึก
    • NF คือจำนวนฟิลด์ของบรรทัดปัจจุบัน
    • OFS แยกยื่นยื่นออก
    • ORS สิ้นสุดบันทึกการส่งออก
    • 1เป็นเคล็ดลับที่จะพูดกับ awk trueและพิมพ์ค่าเริ่มต้น ( $0)

อัปเดต ...

ฉันเกือบจะลืมก็เป็นไปได้ที่จะเปลี่ยนคอลัมน์ต่อไปนี้ทั้งหมด 7 THหนึ่ง

awk  -F',' -v OFS=, '{tmp=$7; for(i=7;i<=NF;i++) $i=$(i+1); $NF=tmp}1 ' "$file"

(1) เนื้อหาจะมีประสิทธิภาพมากขึ้นกว่าOFS $7 "," $7(2) ฉันเชื่อว่า", " $7ไม่ถูกต้องตราบเท่าที่คำถามระบุว่า OP ไม่ต้องการเว้นวรรคหลังเครื่องหมายจุลภาค (และหากข้อมูลที่ป้อนมีช่องว่างหลังจากเครื่องหมายจุลภาคแล้ว$7จะเริ่มต้นด้วยช่องว่างและคุณจะเพิ่มอีกหนึ่งรายการ)
G-Man พูดว่า 'Reinstate Monica'

@ G-Man เป็นส่วนใหญ่เพื่อเสนอความคิดบางสายพันธุ์ ขอบคุณสำหรับจุดฉันเห็นด้วยเกี่ยวกับOFS $7ไม่เพียง แต่แข็งแกร่งมากขึ้น แต่ยิ่งทั่วไป ( "รีบทำให้เสีย" )
Hastur
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.