abstrakcyjny
Drukuj linie bez nowej linii, dodaj nową linię tylko wtedy, gdy jest inna linia do wydrukowania.
$ printf 'one\ntwo\n' |
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
Inne rozwiązania
Jeśli pracujemy z plikiem, możemy po prostu obciąć z niego jeden znak (jeśli kończy się na nowej linii):
removeTrailNewline () {[[$ (tail -c 1 "$ 1")]] || truncate -s-1 "$ 1"; }
Jest to szybkie rozwiązanie, ponieważ musi odczytać tylko jeden znak z pliku, a następnie usunąć go bezpośrednio ( truncate
) bez czytania całego pliku.
Jednak podczas pracy z danymi ze standardu (strumienia) dane muszą zostać odczytane. I jest „konsumowany”, jak tylko zostanie odczytany. Brak powrotu (jak w przypadku obcięcia). Aby znaleźć koniec strumienia, musimy przeczytać do końca strumienia. W tym momencie nie ma możliwości powrotu do strumienia wejściowego, dane zostały już „zużyte”. Oznacza to, że dane muszą być przechowywane w jakiejś formie bufora, dopóki nie dopasujemy końca strumienia, a następnie nie zrobimy czegoś z danymi w buforze.
Najbardziej oczywistym rozwiązaniem jest konwersja strumienia do pliku i przetworzenie tego pliku. Ale pytanie wymaga jakiegoś filtru strumienia. Nie chodzi o wykorzystanie dodatkowych plików.
zmienna
Naiwnym rozwiązaniem byłoby przechwycenie całego wkładu do zmiennej:
FilterOne(){ filecontents=$(cat; echo "x"); # capture the whole input
filecontents=${filecontents%x}; # Remove the "x" added above.
nl=$'\n'; # use a variable for newline.
printf '%s' "${filecontents%"$nl"}"; # Remove newline (if it exists).
}
printf 'one\ntwo' | FilterOne ; echo 1done
printf 'one\ntwo\n' | FilterOne ; echo 2done
printf 'one\ntwo\n\n' | FilterOne ; echo 3done
pamięć
Możliwe jest załadowanie całego pliku do pamięci za pomocą sed. W sed nie można uniknąć końcowego znaku nowej linii w ostatnim wierszu. GNU sed może uniknąć drukowania końcowego znaku nowej linii, ale tylko wtedy, gdy brakuje go w pliku źródłowym. Więc nie, prosty sed nie może pomóc.
Z wyjątkiem GNU awk z -z
opcją:
sed -z 's/\(.*\)\n$/\1/'
Za pomocą awk (dowolnego awk), slurpuj cały strumień i printf
to bez końcowego znaku nowej linii.
awk ' { content = content $0 RS }
END { gsub( "\n$", "", content ); printf( "%s", content ) }
'
Ładowanie całego pliku do pamięci może nie być dobrym pomysłem, może zajmować dużo pamięci.
Dwie linie w pamięci
W awk możemy przetwarzać dwie linie na pętlę, przechowując poprzednią linię w zmiennej i drukując obecną:
awk 'NR>1{print previous} {previous=$0} END {printf("%s",$0)}'
Bezpośrednie przetwarzanie
Ale moglibyśmy zrobić lepiej.
Jeśli wydrukujemy obecną linię bez nowej linii i wydrukujemy nową linię tylko wtedy, gdy istnieje kolejna linia, przetwarzamy jedną linię na raz, a ostatnia linia nie będzie miała nowej linii końcowej:
awk 'NR == 1 {printf ("% s", 0 $); dalej}; {printf („\ n% s”, 0 USD)} ”
Lub napisane w inny sposób:
awk 'NR>1{ print "" }; { printf( "%s", $0 ) }'
Lub:
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'
Więc:
$ printf 'one\ntwo\n' | awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
chomp
, ponieważchomp
usuwa tylko co najwyżej jedną końcową linię.