Szukam czegoś, co zachowuje się jak Perl chomp. Szukam polecenia, które po prostu wypisuje dane wejściowe, minus ostatni znak, jeśli jest to nowy wiersz:
$ printf "one\ntwo\n"| COMMAND_IM_LOOKING_FOR ; echo " done"
one
two done
$ printf "one\ntwo"| COMMAND_IM_LOOKING_FOR ; echo " done"
one
two done
(Podstawianie poleceń w Bash i Zsh usuwa wszystkie końcowe nowe linie, ale szukam czegoś, co usunie co najwyżej jedną końcową nową linię.)
Jeśli potrzebujesz dokładnego odpowiednika chomp, pierwszą metodą, która przychodzi mi do głowy, jest awk, które już opublikował LatinSuD . Dodam kilka innych metod, które nie implementują, chompale implementują niektóre typowe zadania, do których chompczęsto się stosuje.
Po wstawieniu tekstu do zmiennej wszystkie znaki nowej linii na końcu są usuwane. Więc wszystkie te polecenia generują to samo wyjście w jednym wierszu:
Jeśli chcesz dołączyć tekst do ostatniego wiersza pliku lub wyniku polecenia, sedmoże być wygodne. Z GNU sed i większością innych nowoczesnych implementacji działa to nawet, jeśli dane wejściowe nie kończą się na nowej linii¹; nie spowoduje to jednak dodania nowego wiersza, jeśli jeszcze go nie było.
sed '$ s/$/ done/'
¹ Jednak to nie działa ze wszystkimi implementacjami sed: sed jest narzędziem do przetwarzania tekstu, a plik, który nie jest pusty i nie kończy się znakiem nowej linii, nie jest plikiem tekstowym.
Nie jest to dokładnie równoważne chomp, ponieważ chompusuwa tylko co najwyżej jedną końcową linię.
Flimm
@Flimm Tak, najbardziej oczywistym dokładnym odpowiednikiem chompbyłoby rozwiązanie awk, które LatinSuD już opublikowało. Ale w wielu przypadkach chompjest tylko narzędziem do wykonania pracy, a ja zapewniam sposoby wykonywania niektórych typowych zadań. Pozwól, że zaktualizuję moją odpowiedź, aby to wyjaśnić.
Gilles 'SO - przestań być zły'
1
Inne perlpodejście. Ten odczytuje cały zapis do pamięci, więc może nie być dobrym pomysłem dla dużych ilości danych (użyj cuonglm lub awkpodejście do tego):
$ printf "one\ntwo\n"| perl -0777pe's/\n$//'; echo " done"
one
two done
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 -zopcją:
sed -z 's/\(.*\)\n$/\1/'
Za pomocą awk (dowolnego awk), slurpuj cały strumień i printfto bez końcowego znaku nowej linii.
Ł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:
chomp
, ponieważchomp
usuwa tylko co najwyżej jedną końcową linię.chomp
byłoby rozwiązanie awk, które LatinSuD już opublikowało. Ale w wielu przypadkachchomp
jest tylko narzędziem do wykonania pracy, a ja zapewniam sposoby wykonywania niektórych typowych zadań. Pozwól, że zaktualizuję moją odpowiedź, aby to wyjaśnić.Inne
perl
podejście. Ten odczytuje cały zapis do pamięci, więc może nie być dobrym pomysłem dla dużych ilości danych (użyj cuonglm lubawk
podejście do tego):źródło
Zrobiłem to gdzieś z repozytorium github, ale nie mogę znaleźć gdzie
delete-trailing-blank-lines-sed
źródło
abstrakcyjny
Drukuj linie bez nowej linii, dodaj nową linię tylko wtedy, gdy jest inna linia do wydrukowania.
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:
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ą:Za pomocą awk (dowolnego awk), slurpuj cały strumień i
printf
to bez końcowego znaku nowej linii.Ł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ą:
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:
Lub:
Więc:
źródło