bash zmienia swoje zachowanie w zależności od wartości zmiennej „IFS”


18

Kiedy ustawię IFSzmienną na spację, bashtraktuje wiele spacji jako jedną spację ( myprogramjest to program, który wypisuje otrzymane argumenty wiersza poleceń):

IFS=" "
x="hello   hi   world"
./myprogram $x
argv[1] = hello
argv[2] = hi
argv[3] = world

Ale gdy ustawię IFSzmienną na przecinek, bashnie traktuje wielu przecinków jako jednego przecinka:

IFS=","
x="hello,,,hi,,,world"
./myprogram $x
argv[1] = hello
argv[2] = 
argv[3] = 
argv[4] = hi
argv[5] = 
argv[6] = 
argv[7] = world

Dlaczego?


Dla porównania „IFS” oznacza wewnętrzny separator pól .
pr1268

Odpowiedzi:


21

Jest to udokumentowane w man bash. Pojedyncze wystąpienie dowolnego znaku w IFS, który nie jest spacją, ogranicza pole.

Od man bash:

Powłoka traktuje każdy znak IFS jako ogranicznik i dzieli wyniki innych rozszerzeń na słowa, używając tych znaków jako terminatorów pól. Jeżeli IFS nie jest ustawione, czy jego wartość jest dokładnie <space><tab><newline>, domyślny, a następnie sekwencje <space>, <tab>oraz <newline>na początku i na końcu wyników z poprzednich rozszerzeń są ignorowane, a każda sekwencja znaków IFS nie na początku lub na końcu służy do oddzielania słowa. Jeśli IFS ma wartość inną niż domyślna, to sekwencje spacji, tabulacji i nowej linii są ignorowane na początku i na końcu słowa, o ile spacja ma wartość IFS (spacja IFS ). Każdy znak w IFS, który nie jest białą spacją IFS, wraz z dowolnymi sąsiadującymi znakami białych spacji IFS, ogranicza pole. Sekwencja białych znaków IFS jest również traktowana jako separator. Jeśli wartość IFS wynosi null, nie występuje dzielenie słów. [Podkreślenie dodane.]

Przykłady: dzielenie pól

Jeśli IFS nie ma znaków białych znaków, to białe pola są zawarte w polach:

$ ( IFS=',' x='one , two,three'; printf "<%s>\n" $x )
<one >
< two>
<three>

Jeśli IFS ma zarówno spacje, jak i przecinek, to sekwencje spacji, po których następuje przecinek, a następnie sekwencje spacji są traktowane jako pojedynczy separator:

$ ( IFS=' ,' x='one , two,three'; printf "<%s>\n" $x )
<one>
<two>
<three>

Sekwencje przecinków są interpretowane jako sekwencje pustych pól:

$ ( IFS=' ,' x='one,,,two,three'; printf "<%s>\n" $x )
<one>
<>
<>
<two>
<three>

Przykłady: wiodące i końcowe białe znaki

Jeśli IFS nie zawiera białych znaków, wszelkie początkowe i końcowe białe znaki są przechowywane w polach:

$ ( IFS=',' x='  one , two,three  ,'; printf "<%s>\n" $x )
<  one >
< two>
<three  >

Jeśli IFS zawiera spacje, wówczas usuwane są wszystkie wiodące lub końcowe sekwencje spacji:

$ ( IFS=' ,' x='  one , two,three  ,'; printf "<%s>\n" $x )
<one>
<two>
<three>

być może warto również podkreślić „wtedy sekwencje spacji, tabulacji i nowej linii znaków są ignorowane na początku i na końcu słowa, o ile spacja ma wartość IFS”
Jeff Schaller

@JeffSchaller Doskonały pomysł: właśnie dodałem sekcję na ten temat.
John1024,


co jeśli masz plik rozdzielany tabulatorami z pewnymi brakującymi wartościami? tzn. nie chcesz, aby sekwencje kart były traktowane jako pojedyncza karta. Ponadto pola zawierają przecinki, więc nie można ich używać jako separatora. Czy jedynym rozwiązaniem jest użycie innego ogranicznika (nie tabulatorów)?
Davos,

@Davos Dla danych z każdego pola wyznaczonego przez pojedynczą kartę, może być bardziej naturalne użyć innych narzędzi, które łatwo sobie z tym poradzić, takie jak awkz -F'\t'opcji lub cut. Alternatywnie, jeśli masz najnowszą wersję bash, możesz być w stanie przeanalizować pola za readarraypomocą -d$'\t'opcji.
John1024,
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.