Dlaczego podstawianie poleceń powłoki pożera znak nowej linii?


25

Zgodnie z poniższym przykładem i moim ostatnim pytaniem In bash, gdzie zniknął znak końca nowej linii? Chcę wiedzieć, dlaczego tak się dzieje

  x="$(echo -ne "a\nb\n")" ; echo -n "$x" | xxd -p 
# Output is: 610a62 
# The trailing newline from the 'echo' command
#   has been "deleted" by Command Substitution

Zakładam, że musi istnieć jakiś bardzo ważny powód akcji powłoki, mianowicie Zastąpienie polecenia, aby faktycznie usunąć niektóre dane z wyjściowego polecenia, które zastępuje ...
ale nie mogę się tym zająć, ponieważ wydaje się, że tak jest antyteza tego, co ma robić ... tj. przekazać wyjście polecenia z powrotem do procesu skryptu ... Wstrzymywanie jednego znaku wydaje mi się dziwne, ale przypuszczam, że jest rozsądny powód ... Chcę dowiedzieć się, co to za powód ... .


Odpowiedzi:


22

Ponieważ powłoka nie była pierwotnie zamierzana jako pełny język programowania.

Trudno jest usunąć znak końcowy \nz niektórych danych wyjściowych polecenia. Jednak do celów wyświetlania prawie wszystkie polecenia kończą swój wynik \n, więc… musi istnieć prosty sposób na usunięcie go, gdy chcesz go użyć w innym poleceniu. $()Wybrano automatyczne usuwanie z konstrukcją.

Może więc zaakceptujesz to pytanie jako odpowiedź:

Czy możesz znaleźć prosty sposób na usunięcie końcowego, \njeśli nie zostało to zrobione automatycznie w poniższym poleceniu?

> echo The current date is "$(date)", have a good day!

Należy pamiętać, że cytowanie jest wymagane, aby zapobiec rozbiciu podwójnych spacji, które mogą pojawić się w sformatowanych datach.


1
Jeśli to, co mówisz, jest prawdą, byłbym absolutnie oszołomiony. Jeśli istnieje shoptpotrzeba zmiany opisanego domyślnego zachowania, byłbym szczęśliwy ponownie ... Trudno mi myśleć, że Bash / Linux / Unix skaziłby tak krytyczną funkcję, jak „przechwytywanie standardowego” tylko dlatego, dateże nie ma a z opcją / bez „\ n” ... Oczywistym rozwiązaniem byłoby to, że bash (lub inna powłoka) ma shoptdo tego ... Czy znasz jakieś? .. i czy masz jakieś odnośniki, które mówią o tym, dlaczego i dlaczego ten problem? ... i dlaczego nie datema opcji \ n .. Powinno!
Peter.O

2
Podstawienia poleceń pojawiły się w pierwszej wersji powłoki Bourne'a w „7. edycji” Unixa w 1979 roku. To była już duża rewolucja i chyba (nie urodziłem się), że nikt nie dbał o to, by nie podążać za „\ n”, czy nie. Tak, możesz przeczytać stronę podręcznika w mniej niż 10 minut i wydaje się, że do tej pory nic się nie zmieniło w kwestii zastępowania poleceń. Z wyjątkiem preferowanej $()składni alternatywnej.
Stéphane Gimenez

Dzięki .. Pomyślałem, że może to być problem ze spuścizną i na pewno wpływa to na fakt, że lepiej zacznę uważniej obserwować moje zastępstwa dowodzenia. Do dzisiaj musiałem przeżyć na skrzydle modlitwę. Niektóre z tych „tajemniczych wydarzeń”, które spotkałem, zaczynają mieć teraz sens :) ... Więc w odwrotnym przypadku twojej „randkowej” pozycji, jeśli Chcę zachować ciągłość \ n, muszę zakodować coś takiego :( { echo -n "The current date is "; var="$(date; echo -e x)"; var="${var%x}"; echo -n "$var"; echo ", have a good day!"; }.. niech tak będzie :)
Peter.O

PS ponownie mój powyższy komentarz: Oczywiście, po prostu dateby działało, ale użyłem tylko datejako przykładu dowolnego polecenia ..
Peter.O

Twoje „pytanie” o datę było najsilniejszym pomysłem na moje zrozumienie „dlaczego” Zastępstwo Dowodzenia robi to, co robi, więc była to „najlepsza” odpowiedź na moje pytanie ... i na pewno potrzebowałem również innych dobrych odpowiedzi ..
Peter.O

19

Jest to część standardu :

Powłoka powinna rozszerzyć podstawianie poleceń, wykonując polecenie w środowisku podpowłoki (patrz Środowisko wykonywania powłoki ) i zastępując podstawienie polecenia (tekst polecenia wraz z „$ ()” lub cudzysłowami) standardowym wyjściem polecenia, usuwając sekwencje jednego lub więcej <nowej linii> na końcu podstawienia.

Oczywiście norma jest prawdopodobnie napisana w ten sposób, ponieważ tak to kshzrobiło czy coś, ale jest to standard i jest to udokumentowane zachowanie. Jeśli ci się nie podoba, użyj Perla lub czegoś innego, co pozwoli ci zachować końcowe znaki nowej linii.


Ładne podsumowanie .. Dzięki .. i linki z pewnością pomogły
Peter.O

4
Standard jest taki, ponieważ od tego czasu to zrobiła powłoka Bourne'a i każda inna powłoka.
Gilles 'SO - przestań być zły'

6

To ma dla mnie sens. Nowa linia jest tylko na pierwszym miejscu, w normalnym wyjściu polecenia, tak że monit pojawia się po zakończeniu polecenia w nowym wierszu. Nowa linia w większości przypadków nie jest częścią oryginalnego wyjścia, służy do uporządkowania ekranu. Podczas analizowania danych wyjściowych polecenia nowy wiersz na końcu jest zwykle kłopotliwy. Dlaczego wcpolecenie wyświetla 2 wiersze tekstu? Och, nie, wyprowadza jeden, po którym następuje nowa linia. Kiedy parsujesz wc, nie chcesz się martwić, że istnieją 2 linie wyjściowe - tak naprawdę nie ma, jest tylko jedna.


@EightBitTony: Moje pytanie nie ma żadnego związku z wyświetlaniem czegoś w terminalu. To całkiem proste. Jeśli jest nowa linia do wyświetlenia, to wyświetli nową linię. Chodzi tutaj o to, że \nw oryginalnym stdoutstrumieniu jest usuwany przez zastąpienie polecenia. to znaczy. moje oryginalne dane (bardzo ważne dane) usunęły końcowe znaki nowej linii, nawet jeśli dane są zawinięte w „cudzysłowy”. Rozumiem, jak i dlaczego Dzielenie słów działa, ale absolutnie nie mam pojęcia, dlaczego zastępowanie poleceń usuwa tylko nowe wiersze i tylko końcowe .
Peter.O

1
Nic, co powiedziałeś, nie zmienia mojego poglądu. Częściej niż nie, końcowy nowy wiersz w jakimkolwiek wyjściu jest tam wyłącznie do celów wyświetlania, a nie jako część danych.
EightBitTony

Zgadzam się z twoim poglądem i mam cały czas ... Tak, nowa linia na końcu wyjścia jest dla wygody ... ale mój problem nie ma z tym nic wspólnego ... Pytam: Dlaczego Zastępowanie Poleceń siłą go usuwa ? ... Są to dwie różne kwestie .. Może moje pytanie powinno brzmieć: Czy istnieje wbudowane obejście? . ponieważ konieczność kodowania tego problemu przez dodanie zwodniczego znaku zastępczego, do bani! ... Zrobię to, jeśli będę musiał, ale po raz pierwszy przeszkadza mi bash (i jestem w szoku :) .. To robi tak dobrze ...
Peter.O

Jak wspomniano w innej odpowiedzi, jest to część standardu. Dla mnie myślę, że robi to w ten sposób, ponieważ po przetworzeniu danych chcesz tylko zobaczyć dane, a nie wygodny nowy wiersz. Jest tak, ponieważ powłoka oczekuje , że będziesz przetwarzać dane w potoku, a nowy wiersz nie jest danymi. Więcej i musimy wziąć to do dyskusji.
EightBitTony

Dzięki .. Mam całkiem niezły pomysł. Wydaje się, że jest to przypadek zastępowania poleceń po prostu skłaniający się do porzucania nowych linii, zamiast ich utrzymywania. W tym posunięciu musi być trochę mądrości, której „nie czułem” „jeszcze, ale myślałem, że powszechne użycie wszystkich małych liter w nazwach plików jest nieco dziwne / niepotrzebne, ale pomyślałem sobie innego dnia, że ​​jest to całkiem wygodny system. Prawdopodobnie zobacz (głębiej) wierszyk i przyczynę zachowania Zastępstwa Dowodzenia, któregoś dnia ...
Peter.O

1

Napotkałem ten sam problem i znalazłem poniższy przykład. Wydaje się, że zacytowanie pomogło tej sytuacji, a przynajmniej zrobiło to dla mnie:

http://tldp.org/LDP/abs/html/commandsub.html .

dir_listing=`ls -l`
echo $dir_listing     # unquoted

# Expecting a nicely ordered directory listing.

# However, what you get is:
# total 3 -rw-rw-r-- 1 bozo bozo 30 May 13 17:15 1.txt -rw-rw-r-- 1 bozo
# bozo 51 May 15 20:57 t2.sh -rwxr-xr-x 1 bozo bozo 217 Mar 5 21:13 wi.sh

# The newlines disappeared.


echo "$dir_listing"   # quoted
# -rw-rw-r--    1 bozo       30 May 13 17:15 1.txt
# -rw-rw-r--    1 bozo       51 May 15 20:57 t2.sh
# -rwxr-xr-x    1 bozo      217 Mar  5 21:13 wi.sh

2
Oto co się tutaj dzieje. Powiedzmy, że masz zmienną $strzawierającą ciąg "a b c". Jeśli przejdzie $strdo echobez cudzysłowów ( echo $str), echootrzyma a, bi cjako trzy oddzielne argumenty. Twoja powłoka nie dba o odstępy między argumentami. echowypisze te argumenty ze spacjami oddzielającymi każdy, więc otrzymujesz "a b c". Jeśli przekażesz zmienną echoz cudzysłowami wokół niej ( echo "$str"), powłoka zobaczy ją jako jeden argument i echoodbierze ją jako taką, dzięki czemu białe znaki nie zostaną zwinięte. To samo dotyczy znaków nowej linii.

1
Problem, jaki ma oryginalny plakat, polega na tym, że ostatnie znaki (tylko ostatnie) jego poleceń znikają. O ile nie minął -nflagi, echododaje znak wyjściowy nowej linii, więc oba echa w twoim przykładzie mają końcowe znaki nowej linii. Jeśli jednak spróbujesz echo -n $dir_listinglub echo -n "$dir_listing", zobaczysz, że nie ma drukowanego znaku nowej linii z cudzysłowami lub bez nich $dir_listing, co sugeruje, że problem dotyczy substytucji polecenia.

2
tldp to bardzo przestarzałe miejsce do nauki o skryptach powłoki. Zamiast tego wypróbuj Wiki Wooledge Bash. mywiki.wooledge.org/BashGuide
Wildcard

-1

dlaczego echo "hello "(bez cudzysłowów) pożera przestrzeń? wartość znaków IFS jest postrzegana przez powłokę jako ograniczniki, dlatego końcowe znaki (które niczego nie ograniczają) są usuwane. podstawienie polecenia jest po prostu wykonaniem polecenia w podpowłoce, więc jest zgodne z tą samą logiką.


@ Philomath: Zarówno x="hello  "i x="hello     "stracą wszystkie spacje końcowe, jeśli są wyświetlane przez echo $x... Jednak tylko ostatnia nowa linia zastępowania polecenia zostanie utracona.
Peter.O

dziwne, nie widzę żadnej różnicy w moim bashu (weryfikacja za pomocą xxd)
Philomath

Sprawdzam tylko to ... Usuwa wszystkie końcowe znaki ... Eksperymentowałem z IFS, kiedy miałem takie wrażenie, więc anulujmy to :) .. ale pytanie wciąż pozostaje ... To jest podstawowa część rozszczepienie słowo skondensować wielokrotne spacje w jednej przestrzeni, a także całkowicie usunąć początkowe i końcowe spacje .. i można zrozumieć, ale na pewno nie rozumieją, dlaczego, z polecenia Zmiana, to tylko paski \ n, a nie wszystkie spacje (jak z dzieleniem słów) i dlaczego tylko koniec końca? ... a przede wszystkim: „Zastępowanie poleceń” tylko częściowo zastępuje… Dlaczego?
Peter.O

4
IFSnie ma nic wspólnego z usuwaniem nowych linii na końcu podstawiania poleceń.
Gilles 'SO - przestań być zły'
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.