2011-07-25 10 views
12

Robię to ręcznie i po prostu nie mogę tego dłużej robić - mam tysiące linii i myślę, że to jest praca dla sed lub awk.Jak przełączać/obracać co dwie linie za pomocą sed/awk?

Zasadniczo mamy plik tak:

A sentence X 
A matching sentence Y 
A sentence Z 
A matching sentence N 

Wzór ten trwa przez cały plik. Chcę odwrócić każde zdanie i pasujące zdanie, aby cały plik zakończył się w następujący sposób:

A matching sentence Y 
A sentence X 
A matching sentence N 
A sentence Z 

Jakieś wskazówki?

edit: przedłużenia początkowego problemu

Dimitre Radoulov przewidzianego wielką odpowiedź dla początkowego problemu. To jest przedłużenie głównego problemu - kilka dodatkowych szczegółów:

Załóżmy, że mamy zorganizowany plik (z powodu linii sed, którą dał mu Dimitre, plik jest zorganizowany). Jednak teraz chcę zorganizować plik alfabetycznie, ale tylko przy użyciu języka (angielskiego) drugiej linii.

watashi 
me 
annyonghaseyo 
hello 
dobroye utro! 
Good morning! 

Chciałbym uporządkować alfabetycznie zdania w języku angielskim (co drugie zdanie). Biorąc powyższe pod uwagę wejście, powinno to być wyjście:

dobroye utro! 
Good morning! 
annyonghaseyo 
hello 
watashi 
me 

Odpowiedz

8
sed 'N; 
s/\(.*\)\n\(.*\)/\2\ 
\1/' infile 

N - dołącz następną linię wejścia do przestrzeni wzorca
\(.*\)\n\(.*\) - przed zapisaniem części pasujące przestrzeni wzoru jednej i ten po nowej linii.
\2\\ \1 - zamień dwie linie (\ 1 jest pierwszą zapisaną częścią, \ 2 drugą). Użyj zręcznego literału newline do przenośności

W niektórych implementacjach sed można użyć zamiast tego sekwencji escape \ n: \2\n\1.

+0

Dzięki ... to działało jak złoto! Czy byłoby możliwe ponowne uporządkowanie alfabetyczne w oparciu o pierwszą literę pierwszej linii? Wygląda również na to, że rozmiar pliku podskoczył o około 30% po tym, być może dodano jakieś symbole? Nie widzę żadnej białej spacji itp. Usuwam wszystkie końcowe białe znaki za pomocą ":% s/\ s \ + $ //" w vim. edit: Zapisałem dane wyjściowe przez> output.txt, jeśli to ma znaczenie. –

+0

@Google, czy mógłbyś opublikować większą próbkę danych wejściowych i przykład pożądanego wyniku (z uwzględnieniem ostatniego wymogu składania zamówienia)? –

+0

Zaktualizowałem początkowy problem - mam nadzieję, że jest jasne. Jeśli nie daj mi znać. –

2

Zakładając pliku wejściowego takiego:

A sentence X 
Z matching sentence Y 
A sentence Z 
B matching sentence N 
A sentence Z 
M matching sentence N 

można zrobić zarówno wymianę i sortowania z Perl:

perl -lne' 
$_{ $_ } = $v unless $. % 2; 
$v = $_; 
END { 
    print $_, $/, $_{ $_ } 
    for sort keys %_; 
    }' infile 

Wyjście pojawia się:

% perl -lne' 
$_{ $_ } = $v unless $. % 2; 
$v = $_; 
END { 
    print $_, $/, $_{ $_ } 
    for sort keys %_; 
    }' infile 
B matching sentence N 
A sentence Z 
M matching sentence N 
A sentence Z 
Z matching sentence Y 
A sentence X 

Jeśli chcesz zamówić przez pierwszą linię (przed wymianą):

perl -lne' 
$_{ $_ } = $v unless $. % 2; 
$v = $_; 
END { 
    print $_, $/, $_{ $_ } 
    for sort { 
     $_{ $a } cmp $_{ $b } 
     } keys %_; 
    }' infile 

Tak więc, jeśli oryginalny plik wygląda tak:

% cat infile1 
me 
watashi 
hello 
annyonghaseyo 
Good morning! 
dobroye utro! 

Wyjście powinno wyglądać tak:

% perl -lne' 
$_{ $_ } = $v unless $. % 2; 
$v = $_; 
END { 
    print $_, $/, $_{ $_ } 
    for sort { 
    $_{ $a } cmp $_{ $b } 
    } keys %_; 
    }' infile1 
dobroye utro! 
Good morning! 
annyonghaseyo 
hello 
watashi 
me 

Ta wersja powinna obsługiwać zduplikowane rekordy poprawnie:

perl -lne' 
$_{ $_, $. } = $v unless $. % 2; 
$v = $_; 
END { 
    print substr($_, 0, length() - 1) , $/, $_{ $_ } 
    for sort { 
     $_{ $a } cmp $_{ $b } 
     } keys %_; 
    }' infile 

I kolejna wersja, inspi red od rozwiązania zamieszczonych przez Glenn (wymiana rekordowym włączone i zakładając _ZZ_ wzór nie jest obecny w pliku tekstowym):

sed 'N; 
    s/\(.*\)\n\(.*\)/\1_ZZ_\2/' infile | 
    sort | 
     sed 's/\(.*\)_ZZ_\(.*\)/\2\ 
\1/' 
+0

Wow, dzięki! Działa perfekcyjnie - zawarłem to w skrypcie basha. Uratowałeś mi dużo pracy. Wielkie dzięki! –

+0

Po dokładnym sprawdzeniu wygląda na to, że działa dobrze, ale nie obsługuje duplikatów. Czy istnieje sposób, aby z wdziękiem obsługiwać? Wygląda na to, że upuszcza wszelkie duplikaty. –

+0

@Google, masz rację. Dodano stałą wersję. –

6

Pierwsze pytanie:

awk '{x = $0; getline; print; print x}' filename 

następne pytanie: Sortuj według 2nd linii

paste - - < filename | sort -f -t $'\t' -k 2 | tr '\t' '\n' 

których wyjścia:

dobroye utro! 
Good morning! 
annyonghaseyo 
hello 
watashi 
me 
17

Dla pierwszej części pytania, tutaj jest jeden sposób, aby zamienić każdą inną linię ze sobą w sed bez użycia wyrażeń regularnych:

sed -n 'h;n;p;g;p' 

Linia komend -n tłumi automatyczne drukowanie. Polecenie h umieszcza kopie bieżącej linii z przestrzeni wzorów do przestrzeni przechowywania, n czyta w następnej linii do przestrzeni wzorów i p ją drukuje; g kopiuje pierwszą linię z przestrzeni roboczej z powrotem do obszaru wzorów, przywracając pierwszą linię z powrotem do obszaru wzorów, a p ją drukuje.

+1

To jest bardzo ładne! –

+0

niesamowite rozwiązanie! – hovanessyan

+3

Jeśli dane wejściowe mają nieparzystą liczbę linii, ostatnia linia nie zostanie wyprowadzona. Jeśli pożądane jest wyjście z tej linii: 'sed -n '$ p; h; n; p; g; p'' –

Powiązane problemy