Kesmeyi kullanarak sütunları yeniden düzenleyin


135

Aşağıdaki formatta bir dosyam var

Sütun1 Sütun2
str1 1
str2 2
str3 3

Sütunların yeniden düzenlenmesini istiyorum. Aşağıdaki komutu denedim

cut -f2,1 dosya.txt

Komut sütunları yeniden sıralamaz. Neden çalışmadığına dair bir fikriniz var mı?

Teşekkür ederim.

Yanıtlar:


148

For cut(1)man sayfasına:

-B, -c veya -f seçeneklerinden birini ve yalnızca birini kullanın. Her LIST, bir aralıktan veya virgülle ayrılmış birçok aralıktan oluşur. Seçilen girdi okunduğu sırayla yazılır ve tam olarak bir kez yazılır.

Önce alan 1'e ulaşır, böylece yazdırılır, ardından alan 2'ye gelir.

awkBunun yerine kullanın :

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

12
cutBu sezgisel yeniden sıralama komutunu desteklememesi çok kötü . Her neyse, başka bir ipucu: özel giriş ve çıkış alanı ayırıcılarını (gibi ve için ) kullanmak için awk's -FSve -OFSseçeneklerini kullanabilirsiniz . -d--output-delimitercut
malana

12
Üzgünüm, FSbir seçenektir, OFSbir değişkendir. egawk -v OFS=";" -F"\t" '{print $2,$1}'
malana

2
Git Bash'in Windows kullanıcıları için not: Yukarıdaki komuttan tuhaf çıktılar aldıysanız, birbirini geçersiz kılan sütunlar gibi görünüyorsa, suçlu satır başıdır. Dosyanızdaki EOL'yi CRLF'den LF'ye değiştirin.
jakub.g

1
Alternatif olarak, girdi dosyasını değiştirmek istemiyorsanız, | sed 's/\r//' | borulamadan önceawk
jakub.g

2
Bu çok basit ama bazıları için yararlı olabilir, sekmelere göre yeniden sıralamak için boşluğu \ t ile değiştirin ve daha fazla sütun istiyorsanız, örneğin bunu yapabilirsinizawk '{print $4 "\t" $2 "\t" $6 "\t" $7}' file
FatihSarigol

64

Ayrıca birleştirebilir cutve paste:

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

yorumlar yoluyla: Aşağıdakileri yaparak, basizmalardan kaçınmak ve bir kesinti örneği kaldırmak mümkündür:

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

3
Bunun "akıllıca" olarak nitelendirilip nitelendirilmediğinden emin değilim, ancak: f = dosya.txt yapıştırma <(cut -f2 $ f) <(cut -f1 $ f). Ayrıca, bu yöntemin çok sayıda sütununuz olduğunda ve bunlardan büyük blokların etrafında hareket etmek istediğinizde en kolay olduğunu unutmayın.
Michael Rusch

aynı sütundaki değişken uzunluktaki hücrelerle çalışmıyor
kraymer

2
@kraymer Ne demek istiyorsun? cutbenzersiz bir sütun ayırıcınız olduğu sürece değişken uzunluklu sütunlar için iyi çalışır.
üçlü

1
Gereksiz dosyayı ortadan kaldırmak için muhtemelen tee kullanabilirsiniz:
JJW5432

2
bashİzmlerden kaçınmak ve aşağıdakilerden birini cutyaparak bir örneğini kaldırmak mümkündür : paste file.txt file.txt | cut -f2,3
agc

7

sadece kabuğu kullanarak

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

Bu genellikle verimsizdir. Tipik olarak, ilgili Awk betiğinin çok daha hızlı olduğunu göreceksiniz, örneğin. Değerleri alıntılamaya da dikkat etmelisiniz "$col2"ve "$col1"- verilerde kabuk meta karakterleri veya başka saçmalıklar olabilir.
üçlü

7

Perl'i bunun için kullanabilirsiniz:

perl -ane 'print "$F[1] $F[0]\n"' < file.txt
  • -e seçeneği, komutun ardından çalıştırılması anlamına gelir
  • -n satır satır okumak anlamına gelir (dosyayı açın, bu durumda STDOUT ve satırlar üzerinden döngü yapın)
  • -a, bu tür çizgileri @F ("F" - benzeri Field) adlı bir vektöre bölmek anlamına gelir. Perl, 1'den başlayan alanları indeksleyen kesimden farklı olarak 0'dan başlayan vektörleri indeksler.
  • Dosyayı okurken varsayılan boşluk yerine alan ayırıcı olarak desen kullanmak için -F deseni (-F ile desen arasında boşluk olmadan) ekleyebilirsiniz.

Perl çalıştırmanın avantajı (Perl'i biliyorsanız) F üzerinde sütunları yeniden düzenlemekten çok daha fazla hesaplama yapabilmenizdir.


perlrun (1), -a örtük olarak -n olarak ayarlandığını iddia eder, ancak -n set olmadan çalıştırırsam, döngü gibi görünmüyor. Garip.
Trenton

Hangi versiyon? benim için perl -ae printolduğu gibi çalışıyorcat
pwes

5

Kullanarak join:

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

Notlar:

  • -t $'\t'In GNU join daha sezgisel -t '\t' olmadan$ başarısız, ( coreutils v8.28 ve önceki?); bu muhtemelen bir geçici çözümün $gerekli olması gereken bir hatadır . Bakınız: unix birleştirme ayırıcı karakter .

  • joinÜzerinde çalışılan tek bir dosya olmasına rağmen iki dosya adına ihtiyaç duyar. Aynı adı iki kez kullanmak, joinistenen eylemi gerçekleştirmeyi sağlar.

  • Düşük kaynaklara sahip sistemler için join, diğer yanıtlarda kullanılan bazı araçlara göre daha az yer kaplar:

    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

Sadece çok benzer bir şey üzerinde çalışıyorum, uzman değilim ama kullandığım komutları paylaşacağımı düşündüm. Çok sütunlu bir csv'ye sahiptim, sadece 4 sütun gerekiyordu ve sonra onları yeniden düzenlemem gerekiyordu.

Dosyam boru '|' sınırlandırılmıştır, ancak bu değiştirilebilir.

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

Kuşkusuz, gerçekten kaba ve hazır ama buna uyacak şekilde ince ayar yapılabilir!


Bu, sorulan soruya cevap vermiyor. Yığın taşması ruhu içinde, lütfen göndermeden önce bir sorunu yanıtlamak için zaman ayırın.
Bill Gale

0

Sed kullanma

Sütun içeriğini yakalamak ve yeniden sıralamak için temel normal ifadenin iç içe geçmiş alt ifadeleriyle sed kullanın. Bu yaklaşım, bu durumda olduğu gibi, sütunları yeniden sıralamak için sınırlı sayıda kesim olduğunda en uygunudur.

Temel fikir Surround ilginç ile arama desen bölümlerine olduğunu \(ve \)birlikte yedek desende çalınabilir, \#nerede #arama desende alt ifadenin sıralı konumunu temsil eder.

Örneğin:

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

verim:

bar foo

Bir alt ifadenin dışındaki metin taranır, ancak değiştirme dizesinde oynatma için tutulmaz.

Soru sabit genişlikte sütunları tartışmasa da, burada tartışacağız çünkü bu, sunulan herhangi bir çözüm için değerli bir ölçüdür. Basit olması için, çözümün diğer sınırlayıcılar için genişletilebilmesine rağmen dosyanın boşlukla sınırlı olduğunu varsayalım.

Daralan Alanlar

En basit kullanımı göstermek için, birden çok boşluğun tek boşluklara daraltılabileceğini ve ikinci sütun değerlerinin EOL ile (boşluk doldurulmuş değil) sonlandırıldığını varsayalım.

Dosya:

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

Transform:

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

Sütun Genişliklerini Koruma

Şimdi yöntemi, sütunların farklı genişliklerde olmasına izin verirken, sabit genişlikte sütunlara sahip bir dosyaya genişletelim.

Dosya:

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

Transform:

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

Son olarak, soru örneğinde eşit olmayan uzunlukta dizeler olmasa da, bu sed ifadesi bu durumu desteklemektedir.

Dosya:

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

Transform:

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

Kabuk altında diğer sütun yeniden sıralama yöntemleriyle karşılaştırma

  • Şaşırtıcı bir şekilde, bir dosya işleme aracı için awk, bir alandan kaydın sonuna kesmek için pek uygun değildir. Sed'de bu, düzenli ifadeler kullanılarak gerçekleştirilebilir, örneğin , sütunla eşleşecek ifade \(xxx.*$\)nerede xxx?

  • Yapıştır ve kes alt kabukları kullanmak, kabuk komut dosyalarının içinde gerçekleştirilirken zorlaşır. Komut satırından çalışan kod, bir kabuk betiğine getirildiğinde ayrıştırılamaz. En azından bu benim deneyimimdi (beni bu yaklaşıma iten).


0

@Met'ten gelen yanıtı ayrıca Perl kullanarak genişletme:
Giriş ve çıkış TAB ile ayrılmışsa:

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

Giriş ve çıkış boşlukla sınırlandırılmışsa:

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

Burada,
-ePerl'e kodu ayrı bir betik dosyası yerine satır içi olarak aramasını
-n, bir seferde 1. giriş satırını okumasını, satırı okuduktan sonra (benzeri)
-lgiriş kaydı ayırıcısını ( \n* NIX'de) kaldırmasını chompve çıktı eklemesini söyler. (kayıt ayırıcı \nher * NIX) print,
-adizi içine boşluk giriş hattı böler @F,
-F'\t'kombinasyon halinde -aböler diziye giriş sekmelerinde hattı yerine, boşluk @F.

@F[1, 0]dizinin 2. ve 1. elemanlarından oluşan @Fbu sıraya göre dizidir . Perl'deki dizilerin sıfır dizinli olduğunu ve içindeki alanların cut1 dizinli olduğunu unutmayın. Yani içindeki alanlar, @F[0, 1]içindeki ile aynı alanlardır cut -f1,2.

Bu tür notasyonların, yukarıda yayınlanan diğer bazı yanıtlara göre (basit bir görev için uygun olan) daha esnek bir giriş manipülasyonu sağladığını unutmayın. Örneğin:

# 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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.