Jak zapobiec łączeniu ze sobą białych znaków przez „read” z „IFS”? [duplikować]

Nov 26 2020

Weź ten fragment kodu, który wczytuje dane oddzielone |

DATA1="Andreas|Sweden|27"
DATA2="JohnDoe||30"   # <---- UNKNOWN COUNTRY
while IFS="|" read -r NAME COUNTRY AGE; do 
    echo "NAME:    $NAME"; echo "COUNTRY: $COUNTRY";
    echo "AGE:     $AGE"; done<<<"$DATA2"

WYNIK:

IMIĘ: JohnDoe
KRAJ:
WIEK: 30

Powinien działać identycznie jak ten fragment kodu, w którym robimy dokładnie to samo, używając tylko \tjako separatora zamiast|

DATA1="Andreas  Sweden  27"
DATA2="JohnDoe      30"  # <---- THERE ARE TWO TABS HERE
while IFS=$'\t' read -r NAME COUNTRY AGE; do echo "NAME: $NAME";
    echo "COUNTRY: $COUNTRY"; echo "AGE: $AGE";
done<<<"$DATA2"

Ale tak nie jest.

WYNIK:

IMIĘ: JohnDoe
KRAJ: 30
WIEK:

Bash, lub readlub IFSlub jakaś inna część kodu jest globbing razem białe znaki, gdy nie ma. Dlaczego tak się dzieje i jak mogę to naprawić?

Odpowiedzi

3 fpmurphy Nov 26 2020 at 11:00

bashzachowuje się dokładnie tak, jak powinien. Z bashdokumentacji:

Powłoka traktuje każdy znak IFS jako separator i dzieli wyniki innych rozwinięć na słowa dla tych znaków. 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 wyniki poprzednich rozwinięć są ignorowane, a każda sekwencja znaków IFS nie na początku lub na końcu służy do rozgraniczenia słów. Jeśli IFS ma wartość inną niż domyślna, sekwencje białych znaków spacji i tabulacji są ignorowane na początku i na końcu słowa, o ile biały znak znajduje się w wartości IFS (biały znak IFS). Każdy znak w IFS, który nie jest białą spacją IFS, wraz z sąsiednimi białymi znakami IFS, ogranicza pole. Sekwencja białych znaków IFS jest również traktowana jako separator.

Aby obejść tę „funkcję”, możesz zrobić coś takiego:

#!/bin/bash

DATA1="Andreas  Sweden  27"
DATA2="JohnDoe          30"  # <---- THERE ARE TWO TABS HERE

echo "$DATA2" | sed 's/\t/;/g' | while IFS=';' read -r NAME COUNTRY AGE; do echo "NAME: $NAME"
    echo "COUNTRY: $COUNTRY" echo "AGE: $AGE"
done