Jakie jest znaczenie IFS = $ '\ n' w skryptach bash?

163

Na początku skryptu powłoki bash znajduje się następujący wiersz:

IFS=$'\n'

Jakie znaczenie ma ta kolekcja symboli?

Abdul Al Hazred
źródło
3
Zobacz także unix.stackexchange.com/questions/26784/understanding-ifs i przytoczone przez niego pytania.
Gilles
Składnia vim podkreśla to jako błąd; naprawić?
theonlygusti
IFS=$'\n'to bashism (+ inne powłoki, użyj ANSI-C Quoting , aby zapoznać się z obejściami zobacz stackoverflow.com/questions/10748703/…
pevik

Odpowiedzi:

199

IFSoznacza „wewnętrzny separator pól”. Jest on używany przez powłokę do określania sposobu dzielenia słów, tj. Rozpoznawania granic słów.

Wypróbuj to w powłoce, takiej jak bash (inne powłoki mogą obsługiwać to inaczej, na przykład zsh):

mystring="foo:bar baz rab"
for word in $mystring; do
  echo "Word: $word"
done

Wartość domyślna dla IFSskłada się ze spacji (a dokładniej: spacji, tabulacji i nowego wiersza). Każdy znak może być granicą wyrazu. Zatem przy domyślnej wartości IFSpowyższa pętla wyświetli:

Word: foo:bar
Word: baz
Word: rab

Innymi słowy, powłoka uważa, że ​​białe znaki to granica słów.

Teraz spróbuj ustawić IFS=:przed wykonaniem pętli. Tym razem wynik jest następujący:

Word: foo
Word: bar baz rab

Teraz skorupa dzieli się również mystringna słowa - ale teraz traktuje tylko dwukropek jako granicę słów.

Pierwszy znak IFSjest specjalny: służy do rozgraniczenia słów na wyjściu, gdy używana jest $*zmienna specjalna (przykład wzięty z Advanced Bash Scripting Guide , gdzie można również znaleźć więcej informacji na temat zmiennych specjalnych takich jak ta):

$ bash -c 'set w x y z; IFS=":-;"; echo "$*"'
w:x:y:z

Porównać do:

$ bash -c 'set w x y z; IFS="-:;"; echo "$*"'
w-x-y-z

Należy zauważyć, że w obu przykładach, powłoka będzie nadal traktować wszystkich bohaterów :, -a ;jak granice tekstu. Jedyne, co się zmienia, to zachowanie $*.

Inną ważną rzeczą, o której należy wiedzieć, jest sposób traktowania tak zwanych „białych znaków IFS” . Zasadniczo, gdy tylko IFSznaki białych znaków zostaną wstawione, początkowe i końcowe białe znaki są usuwane z ciągu znaków do podziału przed przetworzeniem, a sekwencja kolejnych znaków białych również ogranicza pola. Dotyczy to jednak tylko tych białych znaków, które są w rzeczywistości obecne IFS.

Na przykład spójrzmy na ciąg znaków "a:b:: c d "(spacja końcowa i dwa znaki spacji między ci d).

  1. Z IFS=:byłoby podzielić na cztery pola: "a", "b", ""(pusty ciąg znaków) i " c d "(ponownie, dwie przestrzenie pomiędzy ca d). Zwróć uwagę na wiodące i końcowe białe znaki w ostatnim polu.
  2. Dzięki IFS=' :', że będzie podzielony na pięć obszarów: "a", "b", ""(pusty ciąg znaków), "c"a "d". Nigdzie nie ma wiodących i końcowych białych znaków.

Zwróć uwagę, w jaki sposób wiele kolejnych białych znaków ogranicza dwa pola w drugim przykładzie, podczas gdy wiele kolejnych dwukropków nie (ponieważ nie są to białe znaki).

Jeśli chodzi o IFS=$'\n', to jest ksh93składnia również obsługiwane przez bash, zsh, mkshi FreeBSD sh(z różnic między wszystkich powłok). Cytując stronę podręcznika bash:

Słowa w postaci $ „string” są traktowane specjalnie. Słowo rozwija się do „łańcucha”, z zastąpionymi odwrotnymi znakami znakami, jak określono w standardzie ANSI C.

\njest sekwencją zmiany znaczenia dla nowej linii, więc IFSkończy się na pojedynczym znaku nowej linii.

Tblue
źródło
3
To dobrze, ale moim zdaniem dużo lepiej byłoby przeczytać i zrozumieć specyfikację POSIX niż bashprzewodnik skryptowy lub cokolwiek innego. Zasadniczo w informacjach dostępnych w takich linkach brakuje ważnych informacji. W każdym razie pomija dwa kluczowe punkty dotyczące podziału powłoki - globbing i IFS białe znaki.
mikeserv
@mikeserv Dzięki, dodałem trochę informacji na temat białych znaków IFS. Nie wiedziałem o tym. :)
Tblue
4
Nie tak istotne, ale jeśli jesteś ciekawy, możesz spojrzeć na to, jak unset IFSsprawia, że ​​powłoka zachowuje się zupełnie inaczej niż IFS=. Również pierwszy bajt w IFS jest wyjątkowy "${named_array[*]}"- ale nie ma to znaczenia, gdy rozszerzenie nie jest
cytowane
Kilka dodatkowych punktów: Podział 1- słów, zarządzany przez, $IFSjest jedną z dwóch głównych rzeczy wykonywanych po rozwinięciu niecytowanej zmiennej w kontekście listy (jest to splitczęść split+globoperatora). Drugi to globbing. Podczas korzystania z podziału pracy zwykle musisz wydać set -fpolecenie wyłączenia globczęści.
Stéphane Chazelas,
3
3- $IFSjest także używany przez readwbudowane polecenie
Stéphane Chazelas
22

Wewnątrz pojedynczych cudzysłowów niektóre znaki są specjalnie oceniane. Na przykład \njest przetłumaczony do nowej linii.

Tak więc ten konkretny wiersz przypisuje nowy wiersz do zmiennej IFS. Z kolei IFS jest specjalną zmienną w bash: Separator pól wewnętrznych. Jak man bashmówi to

służy do dzielenia słów po rozwinięciu i dzielenia linii na słowa za pomocą readwbudowanego polecenia. Wartość domyślna to <space><tab><newline>.

choroba
źródło
5
+1 za wzmiankę, dollared single quotesktóra różni się od prostych pojedynczych cytatów.
Snowcrash
2
@Snowcrash +1 za powiedzenie +1 za wzmiankę o pojedynczych cudzysłowach, które różnią się od prostych pojedynczych cudzysłowów . Przepraszam, nic na to nie poradzę :) Ale tak naprawdę warto na to zwrócić uwagę, ponieważ jest to ważne!
Pryftan
1
@Pryftan +1 za +1 za +1 ... wiesz ... to naprawdę ważne.
0xc0de
@ 0xc0de Zdecydowanie się zgadzam! Dziękuję za to! :)
Pryftan
15

Krótko mówiąc, IFS=$'\n'przypisz nowy wiersz \ndo zmiennej IFS.

$'string'konstrukcja jest mechanizmem cytowania, który służy do dekodowania sekwencji ucieczkowych podobnych do ANSI C. Składnia ta pochodzi ksh93, i był przenośny nowoczesnej powłoce jak bash, zsh, pdksh, busybox sh.

Ta składnia nie jest zdefiniowana przez POSIX, ale została zaakceptowana dla wydania SUS 7 .

Cuonglm
źródło
-1

Wolałem wyjaśnić $IFSza pomocą przykładu:
suppsoe chcesz cp lub mv lub inny proces przetwarzania plików, IFS jest pusty przez defualt, gdy twoje pliki mają meta char lub spację, takie jak:
Linux Administration.pdflub Free Software Fundation.ogg, na pewno będziesz mieć problem, ponieważ: Linux rozważa oddzielny parametr i Administracja uważają oddzielny parametr. Więc bash ma built-in variable, Następnie możesz zainicjować IFS==$(echo -en "\n\b"), Następnie bash odrzuci dowolny meta char i spację między nazwą pliku, na przykład:

#!/bin/bash
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
mymusicdir=~/test/dd
find $mymusicdir -name "*" -execdir rename 's/ /_/g' "{}" +
IFS=$SAVEIFS
Zatoka Perska
źródło