Różnica między „ls” a „echo $ (ls)”


27

Rozważ dwie próbki powłoki

$ ls
myDoc.html
SomeDirectory
someDoc.txt

i

$ echo $(ls)
myDoc.html SomeDirectory someDoc.txt

Pierwszy wykonuje, lsktóry, jak rozumiem, dołącza zawartość bieżącego katalogu roboczego do stdoutpliku (co wyświetla terminal). Czy to jest poprawne?

Drugi pobiera wartość lspolecenia (co oznacza zawartość bieżącego katalogu roboczego) i drukuje go do stdoutpliku. Czy to jest poprawne?

Dlaczego te dwa polecenia dają różne wyniki?


1
ponieważ robisz echo. Wyjście ls jako danych wejściowych do polecenia echo.
ankidaemon

Jest to „oczekiwane” zachowanie, które ktoś wkrótce wyjaśni. Czy jesteś jednak pewien, że lssam w sobie nie daje wielu pozycji w wierszu?
roaima

@roaima columnized ls jest pozostałością po „funkcji” bsd. wersje uniksowe drukują jeden wpis w wierszu
Steve Cox

@ SteveCox Nie korzystałem z prawidłowego UNIX-a od wczesnej wersji SVR4, więc moja pamięć jest trochę zamglona. Dziekuje za przypomnienie.
roaima

Dlaczego nie echo *?
jdh8

Odpowiedzi:


70

Po uruchomieniu tego polecenia:

ls

terminal wyświetla wynik działania ls.

Po uruchomieniu tego polecenia:

echo $(ls)

powłoka przechwytuje dane wyjściowe $(ls)i dokonuje na nich podziału słów . Przy ustawieniu domyślnym IFSoznacza to, że wszystkie sekwencje białych znaków, w tym znaki nowego wiersza, są zastępowane jednym pustym miejscem. Dlatego wynik działania echo $(ls)pojawia się w jednym wierszu.

Aby uzyskać zaawansowaną dyskusję na temat podziału słów, zobacz często zadawane pytania Grega .

Pomijanie podziału słów

Powłoka nie wykonuje dzielenia słów na ciągi w cudzysłowie. W ten sposób możesz ukryć dzielenie słów i zachować wieloliniowe wyjście za pomocą:

echo "$(ls)"

ls i wyjście wielowierszowe

Być może zauważyłeś, że lsczasami drukuje więcej niż jeden plik w linii:

$ ls
file1  file2  file3  file4  file5  file6

Jest to ustawienie domyślne, gdy dane wyjściowe są przesyłane lsdo terminala. Gdy dane wyjściowe nie idą bezpośrednio do terminala, lszmienia się domyślnie na jeden plik w linii:

$ echo "$(ls)"
file1
file2
file3
file4
file5
file6

To zachowanie jest udokumentowane w man ls.

Kolejna subtelność: zastępowanie poleceń i końcowe znaki nowej linii

$(...)jest podstawieniem polecenia, a powłoka usuwa końcowe znaki nowego wiersza z wyjścia podstawienia polecenia . Zwykle nie jest to zauważalne, ponieważ domyślnie echododaje jeden nowy wiersz na końcu danych wyjściowych. Tak więc, jeśli stracisz jedną nową linię od końca $(...)i zyskasz jedną z echo, nie ma zmiany. Jeśli jednak dane wyjściowe polecenia kończą się co najmniej 2 znakami nowej linii, a echododaje tylko jeden znak z powrotem, w wynikach brakuje jednego lub więcej znaków nowej linii. Jako przykład możemy użyć printfdo generowania końcowych znaków nowego wiersza. Zauważ, że oba poniższe polecenia, pomimo różnej liczby znaków nowej linii, dają takie same dane wyjściowe z jednej pustej linii:

$ echo "$(printf "\n")"

$ echo "$(printf "\n\n\n\n\n")"

$ 

To zachowanie jest udokumentowane w man bash.

Kolejna niespodzianka: dwukrotne rozwinięcie nazwy ścieżki

Utwórzmy trzy pliki:

$ touch 'file?' file1 file2

Zaobserwować różnicę między ls file?i echo $(ls file?):

$ ls file?
file?  file1  file2
$ echo $(ls file?)
file? file1 file2 file1 file2

W przypadku echo $(ls file?)glob pliku file?jest dwukrotnie rozszerzany , powodując, że nazwy plików file1i file2pojawia się dwukrotnie na wyjściu. Jest tak, ponieważ, jak wskazuje Jeffiekins, interpretacja ścieżki jest wykonywana najpierw przez powłokę przed lsuruchomieniem, a następnie ponownie przed echouruchomieniem.

Drugie rozszerzenie nazwy ścieżki można pominąć, jeśli użyjemy podwójnych cudzysłowów:

$ echo "$(ls file?)"
file?
file1
file2

4
Dodatkowo, używając isattymożesz sprawdzić, czy stdout jest tty, ls używa tego do określenia, czy użyć kolorów, czy nie.
Janus Troelsen

1
Czy cytaty są niezgodne w ostatnim fragmencie kodu? Czy $( )faktycznie zapewniają barierę składni, więc nie trzeba interpolować?
kot

6
@cat $()zapewnia barierę składni.
Random832

2
Jeszcze jedna ważna różnica: jeśli jakakolwiek nazwa pliku zawiera znak wieloznaczny, echopolecenie rozwinie symbol wieloznaczny . Na przykład, jeśli lszwraca plik? plik1 plik2 , a następnie echo $(ls)zwróci plik? plik1 plik2 plik1 plik2 .
Jeffiekins

1
@Jeffiekins echonie rozwija symboli wieloznacznych; powłoka robi to przed przekazaniem wynikowego rozszerzenia jako argumentów do echo.
chepner

0

ls ma świadomość, czy wysyła dane wyjściowe do terminala, czy nie.

Wykonanie lsw wierszu polecenia zapisze na twoim pseudo-tty. Przekierowanie wyjścia lszwykle nie będzie pseudo-tty i lssformatuje wyjście inaczej.

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.