Mam plik tekstowy o następującym formacie. Pierwsza linia to „KEY”, a druga to „VALUE”.
KEY 4048:1736 string
3
KEY 0:1772 string
1
KEY 4192:1349 string
1
KEY 7329:2407 string
2
KEY 0:1774 string
1
Potrzebuję wartości w tym samym wierszu co klucz. Więc wynik powinien wyglądać tak ...
KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1
Byłoby lepiej, gdybym mógł użyć jakiegoś separatora, takiego jak $
lub ,
:
KEY 4048:1736 string , 3
Jak połączyć dwie linie w jedną?
pr
,paste
,awk
,xargs
,sed
ipure bash
! (xargs
jest wolniejszy, wolniejszy niż bash !)Odpowiedzi:
awk:
uwaga, na końcu wyjścia znajduje się pusty wiersz.
sed:
źródło
printf
łańcuchy rozwinięcia, takie jak . Tego niepowodzenia można uniknąć w następujący sposób:%s
$0
'NR%2{printf "%s ",$0;next;}1'
1
po nawiasie zamykającym?paste
nadaje się do tej pracy:źródło
paste
z drugiej strony zachowuje się doskonale. +1.cut
ale zawsze o nich zapominampaste
. Świetnie nadaje się do tego problemu. Musiałem połączyć wszystkie linie ze stdin i zrobiłem to z łatwościąpaste -sd ' ' -
.-
wredne stdin, więcpaste - -
średnie czytanie ze standardowego wejścia, a następnie czytanie ze standardowego wejścia, możesz ułożyć tyle z nich, ile chcesz, czego oczekuję.Alternatywa dla sed, awk, grep:
Jest to najlepsze, gdy chcesz połączyć N wierszy i potrzebujesz tylko wyjścia rozdzielanego spacjami.
Moja pierwotna odpowiedź była
xargs -n2
taka, że oddziela się raczej słowami niż liniami.-d
można użyć do podzielenia wejścia dowolnym pojedynczym znakiem.źródło
-d '\n'
xargs
użytkownikiem, ale nie wiedziałem o tym. Świetna wskazówka.Jest więcej sposobów na zabicie psa niż powieszenie. [1]
W cudzysłowie umieść dowolny separator.
Bibliografia:
źródło
$0
.getline
Komenda chwyta również „następny” linii wejścia i umieszcza go w$0
. Tak więc pierwsza instrukcja przechwytuje pierwszą linię, a polecenie print łączy to, co zostało zapisane w zmiennej,key
z łańcuchem zawierającym przecinek, wraz z wierszem, który został pobrany za pomocągetline
. Jaśniej? :)Oto moje rozwiązanie w bash:
źródło
Chociaż wydaje się, że poprzednie rozwiązania zadziałałyby, gdyby w dokumencie wystąpiła pojedyncza anomalia, wynik zostałby rozpadnięty. Poniżej jest trochę bezpieczniej.
źródło
/KEY/
? Cop
na koniec?/KEY/
wyszukuje linii zKEY
. gdyp
drukuje wynik na zewnątrz. jest bezpieczniejsze, ponieważ stosuje operację tylko na liniach zKEY
w nim.Oto inny sposób
awk
:Jak wskazał Ed Morton w komentarzach, lepiej jest dodać szelki dla bezpieczeństwa i pareny dla przenośności.
ORS
oznacza Output Record Separator. To, co tutaj robimy, to testowanie warunku przy użyciu funkcjiNR
przechowującej numer wiersza. Jeśli modulo ofNR
jest wartością true (> 0), to ustawiamy Separator pola wyjściowego na wartośćFS
(Separator pola), którym domyślnie jest spacja, w przeciwnym razie przypisujemy wartośćRS
(Separator rekordów), czyli znak nowej linii.Jeśli chcesz dodać
,
jako separator, użyj następującego:źródło
ORS
i jest to traktowane tak, jaktrue
skoro ORS pobiera wartość, która nie jest zerem lub ciągiem zerowym, i awks zgaduje, że powinno to być żądło zamiast porównania liczbowego? Czy to coś innego? Naprawdę nie jestem pewien, więc napisałbym to jakoawk '{ORS=(NR%2?FS:RS)}1' file
. Umieściłem w nawiasach trójskładnikowe wyrażenie, aby zapewnić przenośność."ex" to edytor liniowy z możliwością obsługi skryptów, należący do tej samej rodziny co sed, awk, grep itp. Myślę, że może to być to, czego szukasz. Wiele nowoczesnych klonów / następców vi ma również tryb vi.
Ten mówi, dla każdej linii, jeśli pasuje „KEY” wykonać j OIN następnego wiersza. Po wykonująca polecenia (wobec wszystkich liniach), wydać w obrządku i q UIT.
źródło
Jeśli Perl jest opcją, możesz spróbować:
źródło
-0
każe perlowi ustawić separator rekordów ($/)
na null, abyśmy mogli objąć wiele linii w naszym dopasowanym wzorcu. Strony podręcznika są zbyt techniczne, abym mógł zrozumieć, co to oznacza w praktyce.Możesz użyć awk w ten sposób, aby połączyć dwie pary linii:
źródło
Inne rozwiązania wykorzystujące vim (tylko w celach informacyjnych).
Rozwiązanie 1 :
Otwórz plik w vimie
vim filename
, a następnie wykonaj polecenie:% normal Jj
To polecenie jest bardzo łatwe do zrozumienia:
Następnie zapisz plik i wyjdź za pomocą
:wq
Rozwiązanie 2 :
Wykonaj polecenie w powłoce,
vim -c ":% normal Jj" filename
a następnie zapisz plik i zakończ za pomocą:wq
.źródło
norm!
bardziej wytrzymały niżnormal
w przypadku, gdyJ
został przemapowany. +1 za rozwiązanie vim.Możesz także użyć następującego polecenia vi:
źródło
:%g//j
dlatego, że wszystko, czego potrzebujesz, to dopasowanie do wykonania złączenia , a łańcuch pusty jest nadal prawidłowym wyrażeniem regularnym.//
, zostanie użyty poprzedni wzorzec wyszukiwania. Jeśli nie ma poprzedniego wzorca, Vim po prostu zgłasza błąd i nic nie robi. Rozwiązanie Jdamiana działa cały czas.Nieznaczne wahania na odpowiedź Glenn Jackmana za pomocą
paste
: jeśli wartość dla-d
opcji ogranicznika zawiera więcej niż jeden znak,paste
przechodzi po znaków jeden po drugim, i połączono z-s
opcji wciąż robi to podczas przetwarzania tego samego pliku wejściowego.Oznacza to, że możemy użyć tego, co chcemy, jako separatora i sekwencji ucieczki
\n
aby połączyć dwie linie jednocześnie.Za pomocą przecinka:
i znak dolara:
Czego to nie może zrobić, to użyć separatora składającego się z wielu znaków.
Dodatkowo, jeśli
paste
jest zgodny z POSIX, nie zmieni to nowej linii ostatniej linii w pliku, więc dla pliku wejściowego z nieparzystą liczbą linii, jakpaste
nie przyczepi się do znaku separacji w ostatniej linii:źródło
To brzmi jak
źródło
W przypadku, gdy musiałem połączyć dwie linie (dla łatwiejszego przetwarzania), ale zezwolić na dane poza specyfikacją, uznałem to za przydatne
data.txt
wyjście wygląda wtedy następująco:
Convert_data.txt
źródło
Innym podejściem używającym vim byłoby:
Odnosi to
join
(do wiersza poniżej) do wszystkich wierszy, które zawierają to słowoKEY
. Wynik:źródło
Najprostszy sposób jest tutaj:
źródło
-0
zjada cały plik zamiast czytać go wiersz po wierszu;pE
otacza kod pętlą i drukuje dane wyjściowe, zobacz szczegóły w http://perldoc.perl.org/perlrun.html ;^KEY
dopasuj „KEY” na początku wiersza, po którym następuje niechciane dopasowanie czegokolwiek (.*?
) przed sekwencją\s+
dowolnego rodzaju, w tym znaki końca wiersza;(\d+)
które przechwytujemy i później ponownie wstawiamy jako$1
;po którym następuje koniec wiersza
$
.\K
wygodnie wyklucza wszystko po lewej stronie z podstawiania, więc{ $1}
zastępuje tylko 1-2 sekwencje, patrz http://perldoc.perl.org/perlre.html .źródło
Bardziej ogólne rozwiązanie (pozwala na połączenie więcej niż jednej kolejnej linii) jako skrypt powłoki. To dodaje linię między nimi, ponieważ potrzebowałem widoczności, ale można to łatwo naprawić. W tym przykładzie linia „klucz” kończy się na:, a żadne inne nie.
źródło
Wypróbuj następujący wiersz:
Umieść separator pomiędzy
np. jeśli separatorem jest
|
, to:źródło
Możesz użyć w
xargs
ten sposób:źródło
xargs -n 2
ale ta odpowiedź w ogóle tego nie wyjaśnia.