Sắp xếp lại các cột bằng cách sử dụng cắt


134

Tôi đang có một tập tin ở định dạng sau

Cột1 Cột2
str1 1
str2 2
str3 3

Tôi muốn các cột được sắp xếp lại. Tôi đã thử dưới đây lệnh

cắt -f2,1 file.txt

Lệnh không sắp xếp lại các cột. Bất cứ ý tưởng tại sao nó không hoạt động?

Cảm ơn bạn.

Câu trả lời:


148

Đối với cut(1)trang người đàn ông:

Sử dụng một và chỉ một trong số -b, -c hoặc -f. Mỗi DANH SÁCH được tạo thành từ một phạm vi hoặc nhiều phạm vi được phân tách bằng dấu phẩy. Đầu vào được chọn được viết theo cùng thứ tự được đọc và được viết chính xác một lần.

Nó đạt đến trường 1 trước, do đó được in, tiếp theo là trường 2.

Sử dụng awkthay thế:

awk '{ print $2 " " $1}' file.txt

12
Thật tệ cutkhi không hỗ trợ lệnh đặt hàng lại trực quan này. Dù sao, một mẹo khác: bạn có thể sử dụng awkcác tùy chọn -FS-OFStùy chọn để sử dụng các dấu tách trường đầu vào và đầu ra tùy chỉnh (như -d--output-delimitercho cut).
malana

12
Xin lỗi, FSlà một lựa chọn, OFSlà một biến. ví dụawk -v OFS=";" -F"\t" '{print $2,$1}'
malana

2
Lưu ý đối với người dùng Windows của Git Bash: nếu bạn có đầu ra kỳ lạ từ lệnh trên, trông giống như các cột ghi đè lên nhau, việc trả lại vận chuyển là đáng trách. Thay đổi EOL trong tệp của bạn từ CRLF thành LF.
jakub.g

1
Ngoài ra, nếu bạn không muốn thay đổi tệp đầu vào, bạn có thể chuyển nó qua | sed 's/\r//' | trước khi chuyển sangawk
jakub.g

2
Cái này rất đơn giản nhưng có thể hữu ích cho một số người, chỉ cần thay thế không gian bằng cách sắp xếp lại theo tab và trong trường hợp bạn muốn có nhiều cột hơn, bạn có thể làm điều đó chẳng hạnawk '{print $4 "\t" $2 "\t" $6 "\t" $7}' file
FatihSarigol

63

Bạn cũng có thể kết hợp cutpaste:

paste <(cut -f2 file.txt) <(cut -f1 file.txt)

thông qua ý kiến: Có thể tránh bashism và loại bỏ một trường hợp cắt bằng cách thực hiện:

paste file.txt file.txt | cut -f2,3

3
Không chắc chắn nếu điều này đủ điều kiện là "thông minh", nhưng: f = file.txt dán <(cut -f2 $ f) <(cut -f1 $ f). Ngoài ra, tôi lưu ý rằng phương pháp này là dễ nhất khi bạn có nhiều cột và muốn di chuyển xung quanh các khối lớn của chúng.
Michael Rusch

không hoạt động với các ô có độ dài thay đổi trong cùng một cột
kraymer

2
@kraymer Ý bạn là gì? cuthoạt động tốt đối với các cột có chiều dài thay đổi miễn là bạn có một dấu tách cột duy nhất.
tripleee

1
Để loại bỏ tệp dư thừa, bạn có thể sử dụng tee:
JJW5432

2
Có thể tránh các bashisms và loại bỏ một ví dụ cutbằng cách thực hiện: paste file.txt file.txt | cut -f2,3
agc

7

chỉ sử dụng vỏ,

while read -r col1 col2
do
  echo $col2 $col1
done <"file"

Điều này rất thường không hiệu quả. Thông thường, bạn sẽ thấy rằng kịch bản Awk tương ứng nhanh hơn rất nhiều, ví dụ. Bạn cũng nên cẩn thận để trích dẫn các giá trị "$col2""$col1"- có thể có các siêu ký tự shell hoặc các shenanigans khác trong dữ liệu.
tripleee

7

Bạn có thể sử dụng Perl cho điều đó:

perl -ane 'print "$F[1] $F[0]\n"' < file.txt
  • Tùy chọn -e có nghĩa là thực thi lệnh sau nó
  • -n có nghĩa là đọc từng dòng (mở tệp, trong trường hợp này là STDOUT và lặp qua các dòng)
  • -a có nghĩa là chia các dòng như vậy thành một vectơ gọi là @F ("F" - giống như Trường). Perl chỉ mục các vectơ bắt đầu từ 0 không giống như cắt mà chỉ mục các trường bắt đầu từ mẫu 1.
  • Bạn có thể thêm mẫu -F (không có khoảng trắng giữa -F và mẫu ) để sử dụng mẫu làm dấu tách trường khi đọc tệp thay vì khoảng trắng mặc định

Ưu điểm của việc chạy perl là (nếu bạn biết Perl), bạn có thể tính toán nhiều hơn trên F so với sắp xếp lại các cột.


perlrun (1) khiếu nại -a mặc định đặt -n nhưng nếu tôi chạy mà không đặt -n, nó dường như không lặp. lẻ
Trenton

Phiên bản nào? perl -ae printlàm việc như catđối với tôi
pwes

5

Sử dụng join:

join -t $'\t' -o 1.2,1.1 file.txt file.txt

Ghi chú:

  • -t $'\t'Trong GNU join sự trực quan hơn -t '\t' mà không cần sự $thất bại, ( coreutils v8.28 và trước đó?); nó có lẽ là một lỗi mà một cách giải quyết như $vậy là cần thiết. Xem: unix tham gia phân tách char .

  • joincần hai tên tệp, mặc dù chỉ có một tệp đang được xử lý. Sử dụng cùng tên hai lần thủ thuật joinđể thực hiện hành động mong muốn.

  • Đối với các hệ thống có tài nguyên thấp joincung cấp một dấu chân nhỏ hơn một số công cụ được sử dụng trong các câu trả lời khác:

    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

3

Chỉ cần làm việc trên một cái gì đó rất giống nhau, tôi không phải là một chuyên gia nhưng tôi nghĩ tôi sẽ chia sẻ các lệnh tôi đã sử dụng. Tôi đã có một csv nhiều cột mà tôi chỉ yêu cầu 4 cột trong số đó và sau đó tôi cần phải sắp xếp lại chúng.

Tập tin của tôi là ống '|' phân định nhưng có thể được trao đổi.

LC_ALL=C cut -d$'|' -f1,2,3,8,10 ./file/location.txt | sed -E "s/(.*)\|(.*)\|(.*)\|(.*)\|(.*)/\3\|\5\|\1\|\2\|\4/" > ./newcsv.csv

Phải thừa nhận rằng nó thực sự thô và sẵn sàng nhưng nó có thể được điều chỉnh cho phù hợp!


Điều này không trả lời câu hỏi đặt ra. Trong tinh thần của ngăn xếp tràn, xin vui lòng dành thời gian để trả lời một vấn đề trước khi bạn đăng.
Bill Gale

0

Sử dụng sed

Sử dụng sed với các biểu thức con lồng nhau của biểu thức chính quy cơ bản để nắm bắt và sắp xếp lại nội dung cột. Cách tiếp cận này phù hợp nhất khi có một số lần cắt giới hạn để sắp xếp lại các cột, như trong trường hợp này.

Ý tưởng cơ bản là bao quanh các phần thú vị của mẫu tìm kiếm \(\), có thể được phát lại trong mẫu thay thế với \#vị trí #đại diện cho vị trí liên tiếp của biểu hiện phụ trong mẫu tìm kiếm.

Ví dụ:

$ echo "foo bar" | sed "s/\(foo\) \(bar\)/\2 \1/"

sản lượng:

bar foo

Văn bản bên ngoài một biểu thức con được quét nhưng không được giữ lại để phát lại trong chuỗi thay thế.

Mặc dù câu hỏi không thảo luận về các cột có chiều rộng cố định, chúng tôi sẽ thảo luận ở đây vì đây là thước đo xứng đáng cho bất kỳ giải pháp nào được đặt ra. Để đơn giản, giả sử tệp được phân cách bằng dấu cách mặc dù giải pháp có thể được mở rộng cho các dấu phân cách khác.

Sụp đổ không gian

Để minh họa cách sử dụng đơn giản nhất, giả sử rằng nhiều không gian có thể được thu gọn thành các không gian đơn và các giá trị cột thứ hai được kết thúc bằng EOL (và không được đệm không gian).

Tập tin:

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

Biến đổi:

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

Bảo quản chiều rộng cột

Bây giờ chúng ta hãy mở rộng phương thức thành một tệp có các cột có chiều rộng không đổi, trong khi cho phép các cột có độ rộng khác nhau.

Tập tin:

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

Biến đổi:

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

Cuối cùng, mặc dù ví dụ của câu hỏi không có các chuỗi có độ dài không bằng nhau, biểu thức sed này hỗ trợ cho trường hợp này.

Tập tin:

bash-3.2$ cat f3
Column1    Column2
str1       1      
string2    2      
str3       3      

Biến đổi:

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

So sánh với các phương pháp sắp xếp lại cột khác dưới vỏ

  • Đáng ngạc nhiên đối với một công cụ thao tác tập tin, awk không phù hợp để cắt từ một trường đến hết bản ghi. Trong sed, điều này có thể được thực hiện bằng cách sử dụng các biểu thức thông thường, ví dụ: biểu thức \(xxx.*$\)ở đâu xxxđể khớp với cột.

  • Sử dụng dán và cắt các subshells trở nên khó khăn khi thực hiện các kịch bản shell. Mã hoạt động từ dòng lệnh không phân tích cú pháp khi được đưa vào trong tập lệnh shell. Ít nhất đây là kinh nghiệm của tôi (đã đưa tôi đến phương pháp này).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.