ฉันมีไฟล์ในรูปแบบต่อไปนี้
คอลัมน์ 1 คอลัมน์ 2 str1 1 str2 2 str3 3
ฉันต้องการจัดเรียงคอลัมน์ใหม่ ฉันลองคำสั่งด้านล่าง
ตัด -f2,1 file.txt
คำสั่งไม่เรียงลำดับคอลัมน์ใหม่ มีความคิดว่าทำไมจึงไม่ทำงาน?
ขอบคุณ.
ฉันมีไฟล์ในรูปแบบต่อไปนี้
คอลัมน์ 1 คอลัมน์ 2 str1 1 str2 2 str3 3
ฉันต้องการจัดเรียงคอลัมน์ใหม่ ฉันลองคำสั่งด้านล่าง
ตัด -f2,1 file.txt
คำสั่งไม่เรียงลำดับคอลัมน์ใหม่ มีความคิดว่าทำไมจึงไม่ทำงาน?
ขอบคุณ.
คำตอบ:
สำหรับcut(1)
หน้าคน:
ใช้หนึ่งและใช้เพียงหนึ่งใน -b, -c หรือ -f แต่ละ LIST ประกอบด้วยช่วงเดียวหรือหลายช่วงคั่นด้วยลูกน้ำ ข้อมูลที่เลือกจะถูกเขียนในลำดับเดียวกับที่อ่านและเขียนเพียงครั้งเดียว
ถึงฟิลด์ 1 ก่อนเพื่อที่จะพิมพ์ตามด้วยฟิลด์ 2
ใช้awk
แทน:
awk '{ print $2 " " $1}' file.txt
FS
เป็นตัวเลือกOFS
เป็นตัวแปร เช่นawk -v OFS=";" -F"\t" '{print $2,$1}'
| sed 's/\r//' |
awk
awk '{print $4 "\t" $2 "\t" $6 "\t" $7}' file
คุณสามารถรวมcut
และpaste
:
paste <(cut -f2 file.txt) <(cut -f1 file.txt)
ผ่านความคิดเห็น: เป็นไปได้ที่จะหลีกเลี่ยงการทุบตีและลบตัวอย่างหนึ่งของการตัดออกโดยทำ:
paste file.txt file.txt | cut -f2,3
cut
ทำงานได้ดีสำหรับคอลัมน์ที่มีความยาวผันแปรได้ตราบเท่าที่คุณมีตัวคั่นคอลัมน์ที่ไม่ซ้ำกัน
bash
ismsและลบหนึ่งอินสแตนซ์cut
โดยทำ: paste file.txt file.txt | cut -f2,3
ใช้เพียงเปลือก
while read -r col1 col2
do
echo $col2 $col1
done <"file"
"$col2"
และ"$col1"
- อาจมีตัวอักษรเชลล์หรือตัวอักษรอื่น ๆ ในข้อมูล
คุณสามารถใช้ Perl เพื่อ:
perl -ane 'print "$F[1] $F[0]\n"' < file.txt
ข้อดีของการเรียกใช้ perl คือ (ถ้าคุณรู้จัก Perl) คุณสามารถคำนวณ F ได้มากกว่าการจัดเรียงคอลัมน์ใหม่
perl -ae print
ทำงานcat
ให้ฉัน
ใช้join
:
join -t $'\t' -o 1.2,1.1 file.txt file.txt
หมายเหตุ:
-t $'\t'
ในGNU join
ใช้งานง่ายมากขึ้น-t '\t'
โดยไม่เกิดความ$
ล้มเหลว ( coreutils v8.28และรุ่นก่อนหน้า?); อาจเป็นข้อผิดพลาดที่$
ควรมีวิธีแก้ปัญหาเช่นนี้ ดู: ยูนิกซ์เข้าร่วมคั่นถ่าน
join
ต้องการชื่อไฟล์สองชื่อแม้ว่าจะมีไฟล์เดียวที่ใช้งานได้ การใช้ชื่อเดียวกันสองครั้งจะjoin
ทำให้เกิดการกระทำที่ต้องการ
สำหรับระบบที่มีทรัพยากรน้อยจะjoin
มีขนาดเล็กกว่าเครื่องมือบางตัวที่ใช้ในคำตอบอื่น ๆ :
wc -c $(realpath `which cut join sed awk perl`) | head -n -1
43224 /usr/bin/cut
47320 /usr/bin/join
109840 /bin/sed
658072 /usr/bin/gawk
2093624 /usr/bin/perl
เพิ่งทำงานเกี่ยวกับสิ่งที่คล้ายกันมากฉันไม่ใช่ผู้เชี่ยวชาญ แต่ฉันคิดว่าจะแบ่งปันคำสั่งที่ฉันใช้ ฉันมี csv แบบหลายคอลัมน์ซึ่งฉันต้องการเพียง 4 คอลัมน์จากนั้นฉันต้องเรียงลำดับใหม่
ไฟล์ของฉันคือไพพ์ '|' คั่น แต่สามารถสลับออกได้
LC_ALL=C cut -d$'|' -f1,2,3,8,10 ./file/location.txt | sed -E "s/(.*)\|(.*)\|(.*)\|(.*)\|(.*)/\3\|\5\|\1\|\2\|\4/" > ./newcsv.csv
เป็นที่ยอมรับว่ามันหยาบและพร้อมมาก แต่ก็สามารถปรับแต่งให้เหมาะสมได้!
ใช้ sed
ใช้ sed กับนิพจน์ย่อยที่ซ้อนกันของนิพจน์ทั่วไปพื้นฐานเพื่อจับภาพและจัดลำดับเนื้อหาคอลัมน์ใหม่ วิธีนี้เหมาะที่สุดเมื่อมีการตัดจำนวน จำกัด เพื่อจัดเรียงคอลัมน์ใหม่เช่นในกรณีนี้
แนวคิดพื้นฐานคือการล้อมรอบส่วนที่น่าสนใจของรูปแบบการค้นหาด้วย\(
และ\)
ซึ่งสามารถเล่นได้ในรูปแบบการแทนที่โดย\#
ที่#
แสดงตำแหน่งตามลำดับของนิพจน์ย่อยในรูปแบบการค้นหา
ตัวอย่างเช่น:
$ echo "foo bar" | sed "s/\(foo\) \(bar\)/\2 \1/"
อัตราผลตอบแทน:
bar foo
ข้อความภายนอกนิพจน์ย่อยจะถูกสแกน แต่ไม่ถูกเก็บไว้สำหรับการเล่นในสตริงแทนที่
แม้ว่าคำถามจะไม่ได้กล่าวถึงคอลัมน์ความกว้างคงที่ แต่เราจะพูดถึงที่นี่เนื่องจากเป็นการวัดที่คุ้มค่าสำหรับการแก้ปัญหาใด ๆ ที่วางไว้ เพื่อความเรียบง่ายสมมติว่าไฟล์มีการคั่นด้วยช่องว่างแม้ว่าโซลูชันสามารถขยายสำหรับตัวคั่นอื่น ๆ ได้
การยุบช่องว่าง
เพื่อแสดงให้เห็นถึงการใช้งานที่ง่ายที่สุดสมมติว่าสามารถยุบช่องว่างหลายช่องให้เป็นช่องว่างเดียวได้และค่าของคอลัมน์ที่สองจะสิ้นสุดลงด้วย EOL (ไม่ใช่ช่องว่างที่บุนวม)
ไฟล์:
bash-3.2$ cat f
Column1 Column2
str1 1
str2 2
str3 3
bash-3.2$ od -a f
0000000 C o l u m n 1 sp sp sp sp C o l u m
0000020 n 2 nl s t r 1 sp sp sp sp sp sp sp 1 nl
0000040 s t r 2 sp sp sp sp sp sp sp 2 nl s t r
0000060 3 sp sp sp sp sp sp sp 3 nl
0000072
แปลง:
bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f
Column2 Column1
1 str1
2 str2
3 str3
bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f | od -a
0000000 C o l u m n 2 sp C o l u m n 1 nl
0000020 1 sp s t r 1 nl 2 sp s t r 2 nl 3 sp
0000040 s t r 3 nl
0000045
การรักษาความกว้างของคอลัมน์
ตอนนี้เรามาขยายวิธีการไปยังไฟล์ที่มีคอลัมน์ความกว้างคงที่ในขณะที่อนุญาตให้คอลัมน์มีความกว้างต่างกัน
ไฟล์:
bash-3.2$ cat f2
Column1 Column2
str1 1
str2 2
str3 3
bash-3.2$ od -a f2
0000000 C o l u m n 1 sp sp sp sp C o l u m
0000020 n 2 nl s t r 1 sp sp sp sp sp sp sp 1 sp
0000040 sp sp sp sp sp nl s t r 2 sp sp sp sp sp sp
0000060 sp 2 sp sp sp sp sp sp nl s t r 3 sp sp sp
0000100 sp sp sp sp 3 sp sp sp sp sp sp nl
0000114
แปลง:
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2
Column2 Column1
1 str1
2 str2
3 str3
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2 | od -a
0000000 C o l u m n 2 sp C o l u m n 1 sp
0000020 sp sp nl 1 sp sp sp sp sp sp sp s t r 1 sp
0000040 sp sp sp sp sp nl 2 sp sp sp sp sp sp sp s t
0000060 r 2 sp sp sp sp sp sp nl 3 sp sp sp sp sp sp
0000100 sp s t r 3 sp sp sp sp sp sp nl
0000114
สุดท้ายนี้แม้ว่าตัวอย่างของคำถามจะไม่มีสตริงที่มีความยาวไม่เท่ากัน แต่นิพจน์ sed นี้ก็สนับสนุนกรณีนี้
ไฟล์:
bash-3.2$ cat f3
Column1 Column2
str1 1
string2 2
str3 3
แปลง:
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3
Column2 Column1
1 str1
2 string2
3 str3
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3 | od -a
0000000 C o l u m n 2 sp C o l u m n 1 sp
0000020 sp sp nl 1 sp sp sp sp sp sp sp s t r 1 sp
0000040 sp sp sp sp sp nl 2 sp sp sp sp sp sp sp s t
0000060 r i n g 2 sp sp sp nl 3 sp sp sp sp sp sp
0000100 sp s t r 3 sp sp sp sp sp sp nl
0000114
เปรียบเทียบกับวิธีการจัดเรียงคอลัมน์ใหม่ภายใต้เชลล์
น่าแปลกสำหรับเครื่องมือจัดการไฟล์ awk ไม่เหมาะอย่างยิ่งสำหรับการตัดจากฟิลด์ไปยังจุดสิ้นสุดของเร็กคอร์ด ใน sed นี้สามารถทำได้โดยใช้การแสดงออกปกติเช่น\(xxx.*$\)
ที่xxx
เป็นการแสดงออกเพื่อให้ตรงกับคอลัมน์
การใช้วางและตัด subshells จะยุ่งยากเมื่อใช้งานภายในเชลล์สคริปต์ โค้ดที่ทำงานจากบรรทัดคำสั่งไม่สามารถแยกวิเคราะห์เมื่อนำมาไว้ในเชลล์สคริปต์ อย่างน้อยนี่ก็เป็นประสบการณ์ของฉัน (ซึ่งผลักดันให้ฉันเข้าหาแนวทางนี้)
ขยายคำตอบจาก @Met โดยใช้ Perl:
หากอินพุตและเอาต์พุตเป็นแบบคั่นด้วย TAB:
perl -F'\t' -lane 'print join "\t", @F[1, 0]' in_file
หากอินพุตและเอาต์พุตคั่นด้วยช่องว่าง:
perl -lane 'print join " ", @F[1, 0]' in_file
ที่นี่
-e
บอกให้ Perl มองหาโค้ดแบบอินไลน์แทนที่จะอยู่ในไฟล์สคริปต์แยกต่างหาก
-n
อ่านอินพุตทีละ 1 บรรทัด
-l
ลบตัวคั่นบันทึกอินพุต ( \n
บน * NIX) หลังจากอ่านบรรทัด (คล้ายกับchomp
) และเพิ่มเอาต์พุต บันทึกคั่น ( \n
บน * NIX) แต่ละprint
,
-a
แยกสายสัญญาณเข้าบนช่องว่างลงในอาร์เรย์@F
,
-F'\t'
ร่วมกับแยกสายการป้อนข้อมูลบนแท็บแทนช่องว่างลงในอาร์เรย์-a
@F
@F[1, 0]
คืออาร์เรย์ที่ประกอบด้วยองค์ประกอบที่ 2 และ 1 ของอาร์เรย์@F
ตามลำดับนี้ โปรดจำไว้ว่าอาร์เรย์ใน Perl มีการจัดทำดัชนีเป็นศูนย์ในขณะที่ช่องในcut
มีการจัดทำดัชนี 1 รายการ ดังนั้นในเขตนี้เป็นเขตเดียวกับคนใน@F[0, 1]
cut -f1,2
โปรดทราบว่าสัญกรณ์ดังกล่าวช่วยให้สามารถจัดการอินพุตได้อย่างยืดหยุ่นมากกว่าคำตอบอื่น ๆ ที่โพสต์ไว้ด้านบน (ซึ่งเหมาะสำหรับงานง่ายๆ) ตัวอย่างเช่น:
# reverses the order of fields:
perl -F'\t' -lane 'print join "\t", reverse @F' in_file
# prints last and first fields only:
perl -F'\t' -lane 'print join "\t", @F[-1, 0]' in_file
cut
ไม่รองรับคำสั่งสั่งซื้อใหม่ที่ใช้งานง่ายนี้ อย่างไรก็ตามเคล็ดลับอื่น ๆ : คุณสามารถใช้awk
'-FS
และ-OFS
ตัวเลือกเพื่อใช้ตัวคั่นฟิลด์อินพุตและเอาต์พุตที่กำหนดเอง (เช่น-d
และ--output-delimiter
สำหรับcut
)