Susun ulang kolom menggunakan cut

135

Saya memiliki file dalam format berikut

Kolom1 Kolom2
str1 1
str2 2
str3 3

Saya ingin kolom disusun ulang. Saya mencoba perintah di bawah ini

potong -f2, 1 file.txt

Perintah tidak menyusun ulang kolom. Adakah yang tahu mengapa ini tidak berhasil?

Terima kasih.

Boolean
sumber

Jawaban:

148

Untuk cut(1)halaman manual:

Gunakan satu, dan hanya satu dari -b, -c atau -f. Setiap LIST terdiri dari satu rentang, atau banyak rentang yang dipisahkan oleh koma. Input yang dipilih ditulis dalam urutan yang sama dengan yang dibaca, dan ditulis tepat sekali.

Mencapai bidang 1 pertama, sehingga dicetak, diikuti oleh bidang 2.

Gunakan awksebaliknya:

awk '{ print $2 " " $1}' file.txt
Ignacio Vazquez-Abrams
sumber
12
Sayang cutsekali tidak mendukung perintah pemesanan ulang intuitif ini. Pokoknya, tip lain: Anda dapat menggunakan awk's -FSdan -OFSpilihan untuk masukan menggunakan adat dan pemisah lapangan keluaran (seperti -ddan --output-delimiteruntuk cut).
malana
12
Maaf, FSadalah pilihan, OFSadalah variabel. egawk -v OFS=";" -F"\t" '{print $2,$1}'
malana
2
Catatan untuk pengguna Windows dari Git Bash: jika Anda memiliki output aneh dari perintah di atas, tampak seperti kolom saling menimpa, pengembalian carriage yang harus disalahkan. Ubah EOL di file Anda dari CRLF ke LF.
jakub.g
1
Atau jika Anda tidak ingin mengubah file input, Anda dapat | sed 's/\r//' | awk
mengirim
2
Yang ini sangat sederhana tetapi mungkin berguna untuk beberapa orang, cukup ganti spasi dengan \ t untuk menata ulang berdasarkan tab, dan jika Anda menginginkan lebih banyak kolom, Anda dapat melakukannya sebagai contohawk '{print $4 "\t" $2 "\t" $6 "\t" $7}' file
FatihSarigol
64

Anda juga dapat menggabungkan cutdan paste:

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

via komentar: Dimungkinkan untuk menghindari bashisme dan menghapus satu contoh pemotongan dengan melakukan:

paste file.txt file.txt | cut -f2,3
Justin Kaeser
sumber
3
Tidak yakin apakah ini memenuhi syarat sebagai "cerdik", tetapi: f = file.txt tempel <(cut -f2 $ f) <(cut -f1 $ f). Juga, saya perhatikan bahwa metode ini adalah yang termudah ketika Anda memiliki banyak kolom dan ingin bergerak di sekitar blok yang besar.
Michael Rusch
tidak bekerja dengan sel dengan panjang variabel dalam kolom yang sama
kraymer
2
@rayray Apa maksudmu? cutberfungsi dengan baik untuk kolom panjang variabel selama Anda memiliki pemisah kolom yang unik.
tripleee
1
Untuk menghilangkan file yang berlebihan Anda mungkin dapat menggunakan tee:
JJW5432
2
Dimungkinkan untuk menghindari bashisme dan menghapus satu contoh cutdengan melakukan: paste file.txt file.txt | cut -f2,3
AGC
7

hanya menggunakan shell,

while read -r col1 col2
do
  echo $col2 $col1
done <"file"
ghostdog74
sumber
Ini seringkali tidak efisien. Biasanya, Anda akan menemukan bahwa skrip Awk yang sesuai jauh lebih cepat, misalnya. Anda juga harus berhati-hati mengutip nilai-nilai "$col2"dan "$col1"- mungkin ada metacharacters shell atau shenanigans lainnya dalam data.
tripleee
7

Anda dapat menggunakan Perl untuk itu:

perl -ane 'print "$F[1] $F[0]\n"' < file.txt
  • Opsi -e berarti menjalankan perintah setelahnya
  • -n berarti membaca baris demi baris (buka file, dalam hal ini STDOUT, dan lewati baris)
  • -a berarti membagi garis-garis tersebut ke vektor yang disebut @F ("F" - seperti Field). Vektor indeks Perl mulai dari 0 tidak seperti memotong bidang indeks mana mulai dari 1.
  • Anda dapat menambahkan pola -F (tanpa spasi antara -F dan pola ) untuk menggunakan pola sebagai pemisah bidang saat membaca file alih-alih spasi putih default

Keuntungan menjalankan perl adalah bahwa (jika Anda tahu Perl) Anda dapat melakukan lebih banyak perhitungan pada F daripada mengatur ulang kolom.

Bertemu
sumber
perlrun (1) mengklaim -a set secara implisit -n tetapi jika saya menjalankan tanpa -n set, sepertinya tidak akan mengulang. aneh.
Trenton
Versi apa? perl -ae printbekerja seperti catuntuk saya
pwes
5

Menggunakan join:

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

Catatan:

  • -t $'\t'Dalam GNU join yang lebih intuitif -t '\t' tanpa yang $gagal, ( coreutils v8.28 dan sebelumnya?); itu mungkin bug yang $harus diperbaiki. Lihat: unix gabung pemisah char .

  • joinmembutuhkan dua nama file, meskipun hanya ada satu file yang sedang dikerjakan. Menggunakan nama yang sama dua kali trik joinuntuk melakukan tindakan yang diinginkan.

  • Untuk sistem dengan sumber daya rendah joinmenawarkan jejak yang lebih kecil daripada beberapa alat yang digunakan dalam jawaban lain:

    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
agc
sumber
3

Baru saja mengerjakan sesuatu yang sangat mirip, saya bukan ahli tapi saya pikir saya akan membagikan perintah yang telah saya gunakan. Saya memiliki multi-kolom csv yang saya hanya membutuhkan 4 kolom dan kemudian saya perlu memesan ulang.

File saya adalah pipa '|' dibatasi tetapi itu bisa ditukar.

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

Memang benar-benar kasar dan siap tetapi dapat disesuaikan dengan!

Chris Rymer
sumber
Ini tidak menjawab pertanyaan yang diajukan. Dengan semangat stack overflow, silakan berikan waktu untuk menjawab masalah sebelum Anda memposting.
Bill Gale
0

Menggunakan sed

Gunakan sed dengan sub-ekspresi bersarang ekspresi reguler dasar untuk menangkap dan menyusun ulang konten kolom. Pendekatan ini paling cocok ketika ada sejumlah pemotongan untuk menyusun ulang kolom, seperti dalam kasus ini.

Ide dasarnya adalah mengelilingi bagian-bagian yang menarik dari pola pencarian dengan \(dan \), yang dapat diputar kembali dalam pola penggantian dengan \#mana# mewakili posisi sekuensial dari subekspresi dalam pola pencarian.

Sebagai contoh:

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

hasil:

bar foo

Teks di luar subekspresi dipindai tetapi tidak disimpan untuk diputar dalam string pengganti.

Meskipun pertanyaannya tidak membahas kolom lebar tetap, kami akan membahas di sini karena ini adalah ukuran yang layak untuk setiap solusi yang diajukan. Untuk kesederhanaan, mari kita asumsikan file dibatasi oleh ruang meskipun solusinya dapat diperluas untuk pembatas lainnya.

Ruang Runtuh

Untuk mengilustrasikan penggunaan paling sederhana, mari kita asumsikan bahwa banyak spasi dapat diciutkan menjadi spasi tunggal, dan nilai kolom kedua diakhiri dengan EOL (dan bukan spasi empuk).

Mengajukan:

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

Mengubah:

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

Mempertahankan Lebar Kolom

Sekarang mari kita memperluas metode ke file dengan kolom lebar konstan, sementara memungkinkan kolom menjadi lebar berbeda.

Mengajukan:

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

Mengubah:

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

Terakhir meskipun contoh pertanyaan tidak memiliki string dengan panjang yang tidak sama, ungkapan sed ini mendukung kasus ini.

Mengajukan:

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

Mengubah:

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

Bandingkan dengan metode penataan ulang kolom lainnya di bawah shell

  • Anehnya untuk alat manipulasi file, awk tidak cocok untuk memotong dari bidang ke akhir rekaman. Dalam sed ini dapat dilakukan dengan menggunakan ekspresi reguler, misalnya di \(xxx.*$\)mana xxxekspresi untuk mencocokkan kolom.

  • Menggunakan rekatkan dan potong subkulit menjadi sulit saat menerapkan skrip shell di dalam. Kode yang berfungsi dari commandline gagal diurai ketika dibawa ke dalam skrip shell. Setidaknya ini adalah pengalaman saya (yang mendorong saya ke pendekatan ini).

Bill Gale
sumber
0

Memperluas jawaban dari @Met, juga menggunakan Perl:
Jika input dan output dibatasi TAB:

perl -F'\t' -lane 'print join "\t", @F[1, 0]' in_file

Jika input dan output dibatasi spasi:

perl -lane 'print join " ", @F[1, 0]' in_file

Di sini,
-eberi tahu Perl untuk mencari kode sebaris, daripada dalam file skrip terpisah,
-nmembaca baris input 1 sekaligus,
-lmenghapus pemisah rekaman input ( \npada * NIX) setelah membaca baris (mirip dengan chomp), dan menambahkan output merekam pemisah ( \npada * NIX) untuk masing-masing print,
-amembagi jalur input pada spasi putih ke dalam array @F,
-F'\t'dalam kombinasi dengan -amembagi jalur input pada TAB, bukannya spasi putih ke dalam array @F.

@F[1, 0]adalah array yang terdiri dari elemen ke-2 dan ke-1 @F, dalam urutan ini. Ingat bahwa array di Perl diindekskan nol, sedangkan bidang dalam cut1-diindeks. Jadi bidang dalam @F[0, 1]adalah bidang yang sama dengan yang ada dicut -f1,2 .

Perhatikan bahwa notasi tersebut memungkinkan manipulasi input yang lebih fleksibel daripada pada beberapa jawaban lain yang diposting di atas (yang baik untuk tugas sederhana). Sebagai contoh:

# 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
Timur Shtatland
sumber