Mam plik w następującym formacie
Kolumna1 Kolumna2 str1 1 str2 2 str3 3
Chcę, aby kolumny zostały uporządkowane. Spróbowałem poniżej polecenia
cut -f2,1 plik.txt
Polecenie nie zmienia kolejności kolumn. Każdy pomysł, dlaczego nie działa?
Dziękuję Ci.
cut
że nie obsługuje tego intuicyjnego polecenia zmiany kolejności. Tak czy inaczej, kolejna wskazówka: można użyćawk
„s-FS
oraz-OFS
opcje użycie niestandardowego wejścia i wyjścia (separatory polowych jak-d
i--output-delimiter
dlacut
).FS
jest opcja,OFS
to zmienna. np.awk -v OFS=";" -F"\t" '{print $2,$1}'
| sed 's/\r//' |
przed przesłaniem doawk
awk '{print $4 "\t" $2 "\t" $6 "\t" $7}' file
Możesz także łączyć
cut
ipaste
:poprzez komentarze: można uniknąć bashizmów i usunąć jedno wystąpienie cięcia, wykonując:
źródło
cut
działa dobrze w przypadku kolumn o zmiennej długości, o ile masz unikalny separator kolumn.bash
izmów i usunąć jedną instancjęcut
, wykonując:paste file.txt file.txt | cut -f2,3
używając samej powłoki,
while read -r col1 col2 do echo $col2 $col1 done <"file"
źródło
"$col2"
i"$col1"
- w danych mogą znajdować się metaznaki powłoki lub inne sztuczki.Możesz do tego użyć Perla:
perl -ane 'print "$F[1] $F[0]\n"' < file.txt
Zaletą uruchomienia perla jest to, że (jeśli znasz Perla) możesz wykonać znacznie więcej obliczeń na F niż przestawianie kolumn.
źródło
perl -ae print
działa jakcat
dla mnieUżywając
join
:join -t $'\t' -o 1.2,1.1 file.txt file.txt
Uwagi:
-t $'\t'
W GNUjoin
bardziej intuicyjny-t '\t'
bez$
zawiedzie, ( coreutils v8.28 i wcześniej?); prawdopodobnie jest to błąd, którego obejście$
powinno być konieczne. Zobacz: znak separatora łączenia unixowego .join
potrzebuje dwóch nazw plików, mimo że pracujemy tylko nad jednym plikiem. Dwukrotne użycie tej samej nazwy skłaniajoin
do wykonania żądanej czynności.W przypadku systemów o niskich zasobach
join
zajmuje mniej miejsca niż niektóre narzędzia używane w innych odpowiedziach: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
źródło
Właśnie pracowałem nad czymś bardzo podobnym, nie jestem ekspertem, ale pomyślałem, że podzielę się poleceniami, których użyłem. Miałem plik csv z wieloma kolumnami, z którego wymagałem tylko 4 kolumn, a następnie musiałem je zmienić.
Mój plik to pipe '|' rozdzielane, ale można je zamienić.
LC_ALL=C cut -d$'|' -f1,2,3,8,10 ./file/location.txt | sed -E "s/(.*)\|(.*)\|(.*)\|(.*)\|(.*)/\3\|\5\|\1\|\2\|\4/" > ./newcsv.csv
Trzeba przyznać, że jest naprawdę szorstki i gotowy, ale można go dostosować do własnych potrzeb!
źródło
Korzystanie z sed
Użyj seda z zagnieżdżonymi podwyrażeniami podstawowych wyrażeń regularnych, aby przechwycić i zmienić kolejność zawartości kolumny. To podejście najlepiej sprawdza się, gdy istnieje ograniczona liczba cięć w celu zmiany kolejności kolumn, jak w tym przypadku.
Podstawową ideą jest otoczenie interesujących części wzorca wyszukiwania znakami
\(
i\)
, które mogą być odtwarzane we wzorcu zastępczym,\#
gdzie gdzie#
oznacza sekwencyjną pozycję wyrażenia podrzędnego we wzorcu wyszukiwania.Na przykład:
$ echo "foo bar" | sed "s/\(foo\) \(bar\)/\2 \1/"
plony:
Tekst poza podwyrażeniem jest skanowany, ale nie jest zachowywany do odtworzenia w ciągu zastępczym.
Chociaż pytanie nie dotyczyło kolumn o stałej szerokości, omówimy je tutaj, ponieważ jest to godna miara każdego przedstawionego rozwiązania. Dla uproszczenia załóżmy, że plik jest rozdzielany spacjami, chociaż rozwiązanie można rozszerzyć o inne separatory.
Zapadające się spacje
Aby zilustrować najprostsze użycie, załóżmy, że wiele spacji można zwinąć do pojedynczych spacji, a wartości drugiej kolumny są zakończone znakiem EOL (a nie spacjami).
Plik:
Przekształcać:
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
Zachowywanie szerokości kolumn
Rozszerzmy teraz tę metodę do pliku z kolumnami o stałej szerokości, jednocześnie zezwalając na różne szerokości kolumn.
Plik:
Przekształcać:
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
Wreszcie, chociaż przykład pytania nie zawiera łańcuchów o nierównej długości, to wyrażenie sed potwierdza ten przypadek.
Plik:
Przekształcać:
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
Porównanie z innymi metodami zmiany kolejności kolumn pod powłoką
Co zaskakujące, jak na narzędzie do manipulacji plikami, awk nie nadaje się dobrze do wycinania od pola do końca rekordu. W sed można to osiągnąć za pomocą wyrażeń regularnych, np.
\(xxx.*$\)
Gdziexxx
jest wyrażenie pasujące do kolumny.Używanie podpowłok wklejania i wycinania jest trudne podczas implementacji wewnątrz skryptów powłoki. Kod działający z wiersza poleceń nie jest analizowany po przeniesieniu do skryptu powłoki. Przynajmniej takie było moje doświadczenie (które skłoniło mnie do takiego podejścia).
źródło
Rozwijanie odpowiedzi z @Met, również przy użyciu Perla:
Jeśli dane wejściowe i wyjściowe są rozdzielane znakami TAB:
perl -F'\t' -lane 'print join "\t", @F[1, 0]' in_file
Jeśli dane wejściowe i wyjściowe są rozdzielane spacjami:
perl -lane 'print join " ", @F[1, 0]' in_file
Tutaj
-e
mówi Perlowi, aby szukał kodu w tekście, a nie w oddzielnym pliku skryptu,-n
odczytuje wejście 1 wiersz na raz,-l
usuwa separator rekordów wejściowych (\n
na * NIX) po przeczytaniu wiersza (podobnie jakchomp
) i dodaje wyjście separator rekordów (\n
na * NIX) do każdegoprint
,-a
dzieli wiersz wejściowy białymi znakami na tablicę@F
,-F'\t'
w połączeniu z-a
dzieli wiersz wejściowy na TAB-ach zamiast białych znaków na tablicę@F
.@F[1, 0]
to tablica składająca się z drugiego i pierwszego elementu tablicy@F
, w podanej kolejności. Pamiętaj, że tablice w Perlu są indeksowane przez zero, podczas gdy pola wcut
są indeksowane 1. Więc pola w@F[0, 1]
są takie same jak te wcut -f1,2
.Zwróć uwagę, że taka notacja umożliwia bardziej elastyczną manipulację danymi wejściowymi niż w niektórych innych odpowiedziach zamieszczonych powyżej (które są w porządku w przypadku prostego zadania). Na przykład:
# 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
źródło