tutaj-dokument wyświetla błąd „nieoczekiwany koniec pliku”

82

Potrzebuję mojego skryptu, aby wysłać wiadomość e-mail z terminala. Opierając się na tym, co widziałem tutaj i wielu innych miejscach online, sformatowałem to w następujący sposób:

/var/mail -s "$SUBJECT" "$EMAIL" << EOF
Here's a line of my message!
And here's another line!
Last line of the message here!
EOF

Jednak gdy to uruchomię, otrzymuję to ostrzeżenie:

myfile.sh: line x: warning: here-document at line y delimited by end-of-file (wanted 'EOF')

myfile.sh: line x+1: syntax error: unexpected end of file

... gdzie linia x to ostatnia zapisana linia kodu w programie, a linia y to linia, /var/mailw której się znajduje. Próbowałem wymianie EOFz innymi ( ENDOFMESSAGE, FINISH, itd.), Ale bezskutecznie. Prawie wszystko, co znalazłem w Internecie, działa w ten sposób, a ja jestem naprawdę nowy w bash, więc ciężko mi to rozgryźć samodzielnie. Czy ktoś mógłby zaoferować pomoc?

thnkwthprtls
źródło
9
Czy EOFwiersz jest wcięty? Musi być na początku linii.
Barmar
Tak jest, ale tylko w przypadku zagnieżdżenia całej instrukcji. Więc to musi być cała na lewo?
thnkwthprtls
2
Upewnij się również, że nie ma znaków końcowych (w tym powrotu karetki!)
glenn jackman
2
Jeśli wcinasz tylko za pomocą znaków tabulacji, możesz użyć <<-EOF- gnu.org/software/bash/manual/bashref.html#Here-Documents
glenn jackman

Odpowiedzi:

157

EOFŻeton musi być na początku linii, nie można wciąć go wraz z blokiem kodu to idzie z.

Jeśli piszesz <<-EOF, możesz wciąć go, ale musi być wcięty za pomocą Tabznaków, a nie spacji. Więc nadal może nie skończyć się nawet z blokiem kodu.

Również upewnić się, że nie ma spacji poEOF żeton na linii.

Barmar
źródło
2
W powyższym przykładzie kodu token EOF znajduje się na początku wiersza.
Andrew Koster
Nie widzę tego w historii edycji, ale musiało być pierwotnie wcięte, ponieważ nie tylko ja wskazałem na ten problem.
Barmar
Ten błąd pojawia się nawet wtedy, gdy usuwam wszystkie niepotrzebne spacje.
Andrew Koster
Sprawdź znaki CR i użyj, dos2unixaby to naprawić.
Barmar
17

Linia, która zaczyna lub kończy dokument tutaj, prawdopodobnie zawiera jakieś niedrukowalne lub białe znaki (na przykład powrót karetki), co oznacza, że ​​drugi „EOF” nie pasuje do pierwszego i nie kończy dokumentu tutaj, jak powinno. Jest to bardzo częsty błąd i trudny do wykrycia za pomocą samego edytora tekstu. Możesz sprawić, że znaki niedrukowalne będą widoczne, na przykład za pomocą cat:

cat -A myfile.sh

Gdy zobaczysz wyjście z cat -Arozwiązania, będzie oczywiste: usuń obraźliwe znaki.

Joni
źródło
7

Spróbuj usunąć poprzedzające spacje przed EOF: -

/var/mail -s "$SUBJECT" "$EMAIL" <<-EOF

Używanie <tab>zamiast <spaces>ident AND używanie << - EOF działa dobrze.

"-"Usuwa <tabs>, nie <spaces>, ale przynajmniej to działa.

Rahul Tripathi
źródło
2
Pierwsza sugestia nie pomoże (czy przetestowałeś, czy przestrzeń powoduje problem?). Drugi pomoże tylko wtedy, gdy wiersz EOF jest wcięty za pomocą TAB, a nie spacji.
Barmar
Myślę, że żeton kończący nie może mieć wiodących spacji
Rahul Tripathi,
2

Zauważ, że możesz również otrzymać ten błąd, jeśli to zrobisz;

while read line; do
  echo $line
done << somefile

Bo << somefilepowinienem przeczytać < somefilew tym przypadku.

Roel Van de Paar
źródło
0

Oto elastyczny sposób radzenia sobie z wieloma wierszami z wcięciem bez użycia heredoc.

  echo 'Hello!'
  sed -e 's:^\s*::' < <(echo '
    Some indented text here.
    Some indented text here.
  ')
  if [[ true ]]; then
    sed -e 's:^\s\{4,4\}::' < <(echo '
      Some indented text here.
        Some extra indented text here.
      Some indented text here.
    ')
  fi

Kilka uwag na temat tego rozwiązania:

  • jeśli oczekuje się, że treść będzie zawierała proste cudzysłowy, \zastąp je znakami cudzysłowu lub zastąp je podwójnymi cudzysłowami. W tym drugim przypadku należy uważać, aby podobna konstrukcja $(command)została zinterpretowana. Jeśli ciąg zawiera zarówno cudzysłowy proste, jak i cudzysłowy, musisz przynajmniej uciec.
  • podany przykład wypisuje końcową pustą linię, istnieje wiele sposobów na pozbycie się go, nieuwzględnione tutaj, aby ograniczyć do minimum bałagan w propozycji
  • elastyczność wynika z łatwości, z jaką możesz kontrolować, ile miejsca na prowadzenie powinno pozostać lub zniknąć, pod warunkiem, że znasz oczywiście sedno REGEXP.
psychoslave
źródło
0

Kiedy chcę mieć ciągi dokumentacyjne dla moich funkcji bash, używam rozwiązania podobnego do sugestii user12205 w duplikacie tego pytania.

Zobacz, jak definiuję USAGE dla rozwiązania, które:

  • automatyczne formatowanie dobrze dla mnie w moim wybranym IDE (wysublimowane)
  • jest wieloliniowy
  • może używać spacji lub tabulatorów jako wcięć
  • zachowuje wcięcia w komentarzu.
function foo {
    # Docstring
    read -r -d '' USAGE <<'    END'
        # This method prints foo to the terminal.
        #
        # Enter `foo -h` to see the docstring.
        #      It has indentations and multiple lines.
        #
        # Change the delimiter if you need hashtag for some reason.
        # This can include $$ and = and eval, but won't be evaluated
    END


    if [ "$1" = "-h" ]
    then
        echo "$USAGE" | cut -d "#" -f 2 | cut -c 2-
        return
    fi

    echo "foo"
}

Więc foo -hdaje:

This method prints foo to the terminal.

Enter `foo -h` to see the docstring.
     It has indentations and multiple lines.

Change the delimiter if you need hashtag for some reason.
This can include $$ and = and eval, but won't be evaluated

Wyjaśnienie

cut -d "#" -f 2: Pobierz drugą część #rozdzielonych linii. (Pomyśl o csv z „#” jako separatorem, pustą pierwszą kolumnę).

cut -c 2-: Pobierz drugi do końca znak wynikowego ciągu

Zauważ również, że if [ "$1" = "-h" ]obliczane jest tak, Falsejakby nie było pierwszego argumentu, bez błędu, ponieważ staje się pustym ciągiem.

danisheater
źródło
-1

Wraz z innymi odpowiedziami wymienionymi przez Barmara i Joni zauważyłem, że czasami muszę zostawić pustą linię przed i po moim EOF podczas używania <<-EOF.

YOLO ROFL
źródło
1
Nie znajduję na to poparcia ani w teorii, ani w praktyce. Głosowanie za przesądem.
tripleee
1
Miałem multilinię here-doc zawiniętą w pareny do przekierowania cati ze względu na formatowanie, tuż przed moim zamykającym znacznikiem here-doc, musiałem dodać enter przed zamykającym parenem, w przeciwnym razie dostałbym tę niezgodność. Głosowanie za głosami jest całkowicie ważne dla „niektórych” ludzi, choć prawdopodobnie jest to mało prawdopodobny scenariusz.
SidOfc
Nienawidzę przyczyniać się do przesądów, ale miałem też ten problem i dodałem pustą linię po zakończeniu mojego EOF. (ubuntu 19.10 działający w dockerze; to było w skrypcie bootstrap.sh). Pusta linia naprawiła ten błąd.
NDP