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?


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.


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>.


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 .


-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
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.