Jak usunąć znak nowej linii z ciągu w Bash

110

Mam następującą zmienną.

echo "|$COMMAND|"

który powraca

|
REBOOT|

Jak mogę usunąć pierwszą linię?

Matt Leyland
źródło
7
Dlaczego jest to oznaczone jako oszustwo? To pytanie dotyczy usunięcia powrotu karetki. Domniemany duplikat pytania, do którego prowadzi, dotyczy usunięcia białych znaków i tylko przypadkowo wspomina o powrotach karetki.
Michael Scheper,
1
@MichaelScheper zgodził się i ponownie otworzył.
fedorqui 'SO przestań szkodzić'
Terminologia tutaj jest dziwna. Uniksowy znak końca linii \n(szesnastkowo 0x0A) nazywany jest kłamstwem posuwu (LF); różni się od znaku powrotu karetki (CR) \r(szesnastkowo 0x0D), który jest używany w systemie Windows oraz w wielu protokołach sieciowych jako pierwszy bajt w dwubajtowej sekwencji końca linii CR LF.
tripleee

Odpowiedzi:

114

Pod , istnieje kilka bashizmów:

trKomenda może zostać zastąpiona przez // bashowymi :

COMMAND=$'\nREBOOT\r   \n'
echo "|${COMMAND}|"
|
   OOT
|

echo "|${COMMAND//[$'\t\r\n']}|"
|REBOOT   |

echo "|${COMMAND//[$'\t\r\n ']}|"
|REBOOT|

Zobacz Rozwijanie parametrów i CYTOWANIE na stronie podręcznika basha:

man -Pless\ +/\/pattern bash
man -Pless\ +/\\\'string\\\' bash

man -Pless\ +/^\\\ *Parameter\\\ Exp bash
man -Pless\ +/^\\\ *QUOTING bash

Dalej...

Zgodnie z zapytaniem @AlexJordan, spowoduje to usunięcie wszystkich określonych znaków. A co, jeśli $COMMANDzawierają spacje ...

COMMAND=$'         \n        RE BOOT      \r           \n'
echo "|$COMMAND|"
|
           BOOT      
|

CLEANED=${COMMAND//[$'\t\r\n']}
echo "|$CLEANED|"
|                 RE BOOT                 |

shopt -q extglob || { echo "Set shell option 'extglob' on.";shopt -s extglob;}

CLEANED=${CLEANED%%*( )}
echo "|$CLEANED|"
|                 RE BOOT|

CLEANED=${CLEANED##*( )}
echo "|$CLEANED|"
|RE BOOT|

Wkrótce:

COMMAND=$'         \n        RE BOOT      \r           \n'
CLEANED=${COMMAND//[$'\t\r\n']} && CLEANED=${CLEANED%%*( )}
echo "|${CLEANED##*( )}|"
|RE BOOT|

Uwaga: mają extglobopcję włączenia ( shopt -s extglob) w celu użycia *(...)składni.

F. Hauri
źródło
1
To jest sposób na usunięcie wysuwów ze zmiennej w BASH. Inne odpowiedzi niepotrzebnie wywołują dodatkowy proces TR. Przy okazji, ma dodatkową zaletę usuwania spacji końcowych!
ingyhere
1
Zauważ, że usuwa również wewnętrzne spacje z ciągu ... COMMAND="RE BOOT"; echo "|${COMMAND//[$'\t\r\n ']}|"zwraca|REBOOT|
Alex Jordan
2
@AlexJordan Tak, jest to pożądana funkcja : możesz usunąć spację, \naby temu zapobiec: COMMAND="RE BOOT"; echo "|${COMMAND//[$'\t\r\n']}|"powróci |RE BOOT|.
F. Hauri
2
Jak działa wzór ${COMMAND//[$'\t\r\n']}? Myślałem, ${COMMAND//[\t\r\n]}że po prostu zadziała, ale tak się nie stało. Jaki jest $symbol i jakie pojedyncze cudzysłowy?
haridsv
1
Jeśli chodzi o składnię $ '', jest to cytowanie ANSI C (wspomniane w odpowiedzi wjordans).
chuckx
81
echo "|$COMMAND|"|tr '\n' ' '

zamieni znak nowej linii (w POSIX / Unix nie jest to powrót karetki) spacją.

Szczerze mówiąc, pomyślałbym jednak o odejściu od bash na coś bardziej rozsądnego. Lub w pierwszej kolejności unikanie generowania zniekształconych danych.

Hmmm, wygląda na to, że może to być również straszna luka w zabezpieczeniach, w zależności od tego, skąd pochodzą dane.

Robin Green
źródło
Data pochodzi z żądania curl, które znajduje się na tym samym serwerze. Jak mam umieścić to w nowej zmiennej? newvar = $ (echo "| $ COMMAND |" | tr '\ n' '')
Matt Leyland
2
Tak. Ale proszę, powiedz mi, że nie zezwalasz arbitralnym osobom na zdalne ponowne uruchomienie serwera bez hasła ...
Robin Green,
31
Dlaczego nie tr -d '\n'
użyłeś
3
Proszę, wytrzyj bezużyteczne rury! Użyj tr '\n' ' ' <<< "|$COMMAND|"zamiastecho ... | ...
F. Hauri
12
@ F.Hauri: lub bezużyteczne tr's:"|${COMMAND//$'\n'}|"
rici
75

Wyczyść zmienną, usuwając wszystkie powroty karetki:

COMMAND=$(echo $COMMAND|tr -d '\n')
Maxime Chéramy
źródło
22
Czy to nie powoduje zmian w linii? Czy nie powinno być tr -d '\r'zamiast tego?
Izzy
3
Powtórzenie niezakomentowanej zmiennej usuwa wszystkie znaki IFS (domyślnie nowa linia, spacja, tabulator). Więc jeśli masz zamiar to zrobić, powinieneś być świadomy, że wszystkie znaki IFS są odrzucane i nie potrzebujesz tr. Po prostu COMMAND=$(echo $COMMAND)da podobny efekt. Jest to prawdopodobnie diabelski pomiot, który wywołuje nowy proces, ale wciąż jest dość krótki i słodki dla ludzkich oczu, a jeśli masz sekundę lub dwie do stracenia w swoim życiu, możesz zechcieć przyjąć uderzenie :-).
Mike S
Zaktualizowałem pytanie tak, aby brzmiało „wysuw wiersza”, ponieważ jego przykład pokazał, że był to nowy wiersz, a nie powrót karetki. Ta odpowiedź jest nadal poprawna, ale może powinna zostać zaktualizowana, aby powiedzieć „przesunięcie wiersza” zamiast „powrót karetki”?
Benjamin W.,
8

Używając bash:

echo "|${COMMAND/$'\n'}|"

(Zwróć uwagę, że znak kontrolny w tym pytaniu to `` nowa linia '' ( \n), a nie powrót karetki ( \r); ten ostatni miałby wynik REBOOT|w jednym wierszu).

Wyjaśnienie

Używa rozszerzenia parametrów powłoki Bash ${parameter/pattern/string}:

Wzór rozpręża się w celu wytworzenia wzoru jak w rozszerzania nazw. Parametr jest rozwijany, a najdłuższe dopasowanie wzorca do jego wartości jest zastępowane ciągiem . [...] Jeśli łańcuch jest pusty, dopasowania do wzorca są usuwane, a wzorzec / następujący po nim można pominąć.

Używa również konstrukcji $'' cytowania ANSI-C, aby określić nowy wiersz jako $'\n'. Bezpośrednie użycie znaku nowej linii również by działało, choć mniej ładne:

echo "|${COMMAND/
}|"

Pełny przykład

#!/bin/bash
COMMAND="$'\n'REBOOT"
echo "|${COMMAND/$'\n'}|"
# Outputs |REBOOT|

Lub używając nowych linii:

#!/bin/bash
COMMAND="
REBOOT"
echo "|${COMMAND/
}|"
# Outputs |REBOOT|
wjordan
źródło
6

Dla mnie zadziałało echo $testVar | tr "\n" " "

Gdzie testVar zawierał moje dane wyjściowe zmiennej / skryptu

Prachi
źródło
4

Dodanie odpowiedzi, aby pokazać przykład usuwania wielu znaków, w tym \ r używania tr i używania seda. I zilustrowanie za pomocą zrzutu heksowego.

W moim przypadku stwierdziłem, że polecenie kończące się awk print ostatniej pozycji |awk '{print $2}'w linii zawierało znak powrotu karetki \ r oraz cudzysłowy.

Zwykłem sed 's/["\n\r]//g'usuwać zarówno znak powrotu karetki, jak i cudzysłowy.

Mógłbym też użyć tr -d '"\r\n'.

Warto zauważyć, że sed -zjest potrzebny, jeśli ktoś chce usunąć \ n znaki nowego wiersza.

$ COMMAND=$'\n"REBOOT"\r   \n'

$ echo "$COMMAND" |hexdump -C
00000000  0a 22 52 45 42 4f 4f 54  22 0d 20 20 20 0a 0a     |."REBOOT".   ..|

$ echo "$COMMAND" |tr -d '"\r\n' |hexdump -C
00000000  52 45 42 4f 4f 54 20 20  20                       |REBOOT   |

$ echo "$COMMAND" |sed 's/["\n\r]//g' |hexdump -C
00000000  0a 52 45 42 4f 4f 54 20  20 20 0a 0a              |.REBOOT   ..|

$ echo "$COMMAND" |sed -z 's/["\n\r]//g' |hexdump -C
00000000  52 45 42 4f 4f 54 20 20  20                       |REBOOT   |

I to jest istotne: co to jest powrót karetki, nowy wiersz i wysuw strony?

  • CR == \ r == 0x0d
  • LF == \ n == 0x0a
gaoithe
źródło
3

Możesz po prostu użyć echo -n "|$COMMAND|".

Paulo
źródło
2

Jeśli używasz basha z włączoną opcją extglob, możesz usunąć tylko końcowe białe znaki poprzez:

shopt -s extglob
COMMAND=$'\nRE BOOT\r   \n'
echo "|${COMMAND%%*([$'\t\r\n '])}|"

To daje:

|
RE BOOT|

Lub zamień %% na ##, aby zastąpić tylko początkowe białe znaki.

zeroimpl
źródło
1
To działało jak urok z dziwnymi wynikami, które otrzymałem z polecenia dockera. Wielkie dzięki!
develCuy