Jakie jest dokładne znaczenie IFS = $ '\ n'?

125

Jeśli poniższy przykład, który ustawia IFSzmienną środowiskową na znak wysuwu wiersza ...

IFS=$'\n'
  • Co dokładnie oznacza znak dolara ?
  • Co robi w tym konkretnym przypadku?
  • Gdzie mogę przeczytać więcej na temat tego konkretnego zastosowania (Google nie zezwala na znaki specjalne w wyszukiwaniu i nie wiem, czego szukać w innym przypadku)?

Wiem, co to IFSjest zmienna środowiskowa i jaki jest jej \nznak ( nowy wiersz), ale dlaczego nie użyć następującego formularza: IFS="\n"(który nie działa)?

Na przykład, jeśli chcę zapętlić każdy wiersz pliku i chcę użyć pętli for, mogę to zrobić:

for line in (< /path/to/file); do
    echo "Line: $line"
done

Jednak to nie zadziała poprawnie, chyba że IFSzostanie ustawione na znak wysuwu wiersza. Aby to zadziałało, musiałbym to zrobić:

OLDIFS=$IFS
IFS=$'\n'
for line in (< /path/to/file); do
    echo "Line: $line"
done
IFS=$OLDIFS

Uwaga: nie potrzebuję innego sposobu na zrobienie tego samego, znam już wiele innych ... Ciekawi mnie tylko to $'\n'i zastanawiałem się, czy ktoś mógłby mi to wyjaśnić.

Yanick Girouard
źródło

Odpowiedzi:

161

Zwykle bashnie interpretuje sekwencji ucieczki w literałach łańcuchowych. Więc jeśli napiszesz \nlub "\n"lub '\n', to nie jest podział wiersza - jest to litera n(w pierwszym przypadku) lub ukośnik odwrotny, po którym następuje litera n(w pozostałych dwóch przypadkach).

$'somestring'jest składnią literałów łańcuchowych z sekwencjami ucieczki . W przeciwieństwie do tego '\n', w $'\n'rzeczywistości jest to przełom.

sepp2k
źródło
2
Niezupełnie tak - \nto po prostu (uciekana) litera n. Masz rację '\n'i "\n"masz odwagę, po której następuje n.
Roman Cheplyaka
15
Zauważ, że $'\n'jest to specyficzne dla basha - nie będzie działać w powłoce POSIX ( /bin/sh). Aby uzyskać ten sam efekt w sposób zgodny z POSIX, możesz wpisać IFS=', a następnie nacisnąć klawisz Return, aby wpisać rzeczywisty znak nowej linii, a następnie wpisać zamknięcie'
Richard Hansen
23
IFS=$(echo -e '\n')powinien również zrobić to w sposób zgodny z POSIX.
Vineet
12
@Vineet - dało mi to przerwę, aby zakwestionować pozytywny komentarz. Chociaż jest to poprawne dla Posixa, nie działa - Operatory podstawiania poleceń w bash usuwają wszystkie końcowe znaki nowej linii. Zobacz to, aby uzyskać więcej szczegółów .
Digital Trauma,
9
@DigitalTrauma Myślę, że nie jest to nawet POSIX: -enie jest zdefiniowane i \nbez -edziała jako rozszerzenie XSI: pubs.opengroup.org/onlinepubs/9699919799/utilities/… . printf '\n'skały;)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
21

Wystarczy dać konstrukt swoją oficjalną nazwę : ciągi postaci $'...'nazywane są ANSI C cudzysłowów .

Oznacza to, że podobnie jak w łańcuchach [ANSI] C, sekwencje specjalne z odwrotnym ukośnikiem są rozpoznawane i rozszerzane do ich dosłownego odpowiednika (pełna lista obsługiwanych sekwencji specjalnych znajduje się poniżej).

Po tym rozwinięciu $'...'łańcuchy zachowują się tak samo jak '...'łańcuchy - tj. Są traktowane jako literały NIE podlegające żadnym [dalszym] rozszerzeniom powłoki .

Na przykład, $'\n'interpretuje się do dosłownego znaku nowej linii - czego nie może zrobić zwykły literał ciągu basha (czy '...'lub "..."). [1]

Inną interesującą cechą jest to, że ciągi znaków ANSI w cudzysłowach C mogą uciec '(pojedyncze cudzysłowy)\' , ponieważ '...'(zwykłe ciągi w apostrofach) nie mogą:

echo $'Honey, I\'m home' # OK; this cannot be done with '...'

Lista obsługiwanych sekwencji ucieczki :

Sekwencje specjalne z ukośnikiem odwrotnym, jeśli występują, są dekodowane w następujący sposób:

\ a alert (dzwonek)

\ b backspace

\ e \ E znak zmiany znaczenia (nie ANSI C)

\ f form feed

\ n nowa linia

\ r powrót karetki

\ t tabulator poziomy

\ v tabulator pionowy

\ ukośnik wsteczny

pojedynczy cudzysłów

\" cudzysłów

\ nnn ośmiobitowy znak, którego wartością jest wartość ósemkowa nnn (jedna do trzech cyfr)

\ xHH ośmiobitowy znak, którego wartością jest wartość szesnastkowa HH (jedna lub dwie cyfry szesnastkowe)

\ uHHHH znak Unicode (ISO / IEC 10646), którego wartością jest wartość szesnastkowa HHHH (od jednej do czterech cyfr szesnastkowych)

\ UHHHHHHHH znak Unicode (ISO / IEC 10646), którego wartością jest wartość szesnastkowa HHHHHHHH (od jednej do ośmiu cyfr szesnastkowych)

\ cx znak kontrolny-x

Rozszerzony wynik jest podawany w apostrofy, tak jakby znak dolara nie był obecny.


[1] Możesz jednak osadzić rzeczywiste znaki nowej linii w łańcuchach „…” i „…”; tj. można zdefiniować ciągi obejmujące wiele linii.

mklement0
źródło
16

Z http://www.linuxtopia.org/online_books/bash_guide_for_beginners/sect_03_03.html :

Słowa w postaci „$ 'STRING'” są traktowane w specjalny sposób. Słowo rozwija się do łańcucha, w którym znaki poprzedzone odwrotnym ukośnikiem są zastępowane zgodnie ze standardem ANSI-C. Sekwencje ucieczki z odwrotnym ukośnikiem można znaleźć w dokumentacji Bash

Wydaje mi się, że wymusza to na skrypcie ucieczkę z wysuwu wiersza do odpowiedniego standardu ANSI-C.

Brad Swerdfeger
źródło
8

Odzyskiwanie domyślnego IFS OLDIFS=$IFS- nie jest to konieczne. Uruchom nowy IFS w podpowłoce, aby uniknąć zastąpienia domyślnego IFS:

ar=(123 321); ( IFS=$'\n'; echo ${ar[*]} )

Poza tym nie wierzę, że w pełni odzyskasz stary IFS. Powinieneś cytować go podwójnie, aby uniknąć łamania wiersza, takiego jak OLDIFS="$IFS".

Marek
źródło
2
to naprawdę przydatna technika. Używałem go do muszli czystszego dołączyć op: args=$(IFS='&'; echo "$*"). przywrócenie IFSgo $' \t\n'w sposób przyjazny dla powłoki Bourne'a to nie lada wyczyn.
jeberle
Re Besides I don't really believe you recover the old IFS fully: podział na słowa jest nie wykonywane na RHS przypisania zmiennych (ale usuwanie cytat), tak OLDIFS=$IFSi OLDIFS="$IFS"zachowują się tak samo.
mklement0
3

Ciągi w cudzysłowie ANSI C są kluczowym punktem. Dzięki @ mklement0.

Możesz przetestować ciągi znaków ANSI w cudzysłowie C za pomocą polecenia od.

echo -n $'\n' | od -c
echo -n '\n' | od -c
echo -n $"\n" | od -c
echo -n "\n" | od -c

Wyjścia:

0000000  \n  
0000001

0000000   \   n   
0000002

0000000   \   n   
0000002

0000000   \   n   
0000002

Możesz jasno poznać znaczenie dzięki wynikom.

Duża tarcza
źródło
-7

To jak pobieranie wartości ze zmiennej:

VAR='test'
echo VAR
echo $VAR

są różne, więc znak dolara zasadniczo ocenia zawartość.

Pieter
źródło
6
Nie ma to nic wspólnego ze zmiennymi. $'FOO'(w przeciwieństwie do $FOOtego, o co nie chodziło w tym pytaniu) jest ciągiem znaków. Jeśli wykonasz echo $'VAR', zobaczysz, że drukuje łańcuch VAR, a nie test.
wrzesień