here-document da el error 'final inesperado del archivo'

81

Necesito mi script para enviar un correo electrónico desde la terminal. Basado en lo que he visto aquí y en muchos otros lugares en línea, lo formateé así:

/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

Sin embargo, cuando ejecuto esto, aparece esta advertencia:

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

... donde la línea x es la última línea de código escrita en el programa, y ​​la línea y es la línea que /var/mailcontiene. He intentado reemplazar EOFcon otras cosas ( ENDOFMESSAGE, FINISH, etc.), pero en vano. Casi todo lo que he encontrado en línea lo ha hecho de esta manera, y soy realmente nuevo en bash, por lo que me está costando descubrirlo por mi cuenta. ¿Alguien podría ofrecer alguna ayuda?

thnkwthprtls
fuente
9
¿La EOFlínea tiene sangría? Tiene que estar al principio de la línea.
Barmar
Lo es, pero solo en la medida en que toda la declaración esté anidada. ¿Entonces tiene que estar completamente a la izquierda?
thnkwthprtls
2
Además, asegúrese de que no haya caracteres finales (¡incluido el retorno de carro!)
glenn jackman
2
Si aplica sangría solo con caracteres de tabulación, puede usar <<-EOF- gnu.org/software/bash/manual/bashref.html#Here-Documents
glenn jackman

Respuestas:

153

El EOFtoken debe estar al principio de la línea, no puede sangrarlo junto con el bloque de código al que va.

Si escribe <<-EOF, puede sangrarlo, pero debe hacerlo con Tabcaracteres, no con espacios. Por lo tanto, es posible que aún no termine ni siquiera con el bloque de código.

También asegúrese de no tener espacios en blanco después del EOFtoken en la línea.

Barmar
fuente
2
En el ejemplo de código anterior, el token EOF está al principio de la línea.
Andrew Koster
No lo veo en el historial de ediciones, pero debe haber sido sangrado originalmente, porque no fui el único en señalar este problema.
Barmar
Recibo este error incluso cuando elimino todos los espacios en blanco innecesarios.
Andrew Koster
Compruebe si hay caracteres CR y utilícelo dos2unixpara solucionarlo.
Barmar
17

La línea que comienza o termina aquí-doc probablemente tiene algunos caracteres no imprimibles o de espacio en blanco (por ejemplo, retorno de carro) lo que significa que el segundo "EOF" no coincide con el primero, y no termina el here-doc como debería. Este es un error muy común y difícil de detectar con solo un editor de texto. Puede hacer visibles los caracteres no imprimibles, por ejemplo, con cat:

cat -A myfile.sh

Una vez que vea el resultado de cat -Ala solución, será obvio: elimine los caracteres ofensivos.

Joni
fuente
7

Intente eliminar los espacios anteriores antes de EOF: -

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

Usar en <tab>lugar de <spaces>para ident Y usar << - EOF funciona bien.

El "-"quita el <tabs>, no <spaces>, pero al menos funciona este.

Rahul Tripathi
fuente
2
La primera sugerencia no ayudará (¿probó para ver si el espacio causa un problema?). El segundo solo ayudará si la línea EOF está sangrada con TAB, no con espacios.
Barmar
Creo que la ficha de terminación no debe tener espacios
iniciales
2

Tenga en cuenta que también se puede obtener este error si hace esto;

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

Porque << somefiledebería leerse < somefileen este caso.

Roel Van de Paar
fuente
0

Esta es una forma flexible de tratar con múltiples líneas con sangría sin usar 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

Algunas notas sobre esta solución:

  • si se espera que el contenido tenga comillas simples, utilícelas para escapar \o reemplace los delimitadores de cadena con comillas dobles. En el último caso, tenga cuidado de que $(command)se interprete la construcción como . Si la cadena contiene comillas simples y dobles, tendrá que escapar al menos del tipo.
  • el ejemplo dado imprime una línea vacía al final, hay numerosas formas de deshacerse de ella, no incluidas aquí para mantener la propuesta al mínimo desorden
  • la flexibilidad proviene de la facilidad con la que puede controlar cuánto espacio principal debe permanecer o desaparecer, siempre que sepa algo de sed REGEXP, por supuesto.
psicoslave
fuente
0

Cuando quiero tener docstrings para mis funciones de bash, uso una solución similar a la sugerencia de user12205 en un duplicado de esta pregunta.

Vea cómo defino USO para una solución que:

  • auto-formatea bien para mí en mi IDE de elección (sublime)
  • es multilínea
  • puede usar espacios o tabulaciones como sangría
  • conserva las sangrías dentro del comentario.
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"
}

Entonces foo -hrinde:

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

Explicación

cut -d "#" -f 2: Recupera la segunda parte de las #líneas delimitadas. (Piense en un csv con "#" como delimitador, la primera columna vacía).

cut -c 2-: Recupera el segundo carácter al final de la cadena resultante

También tenga en cuenta que se if [ "$1" = "-h" ]evalúa como Falsesi no hubiera un primer argumento, sin error, ya que se convierte en una cadena vacía.

danisheater
fuente
-1

Junto con las otras respuestas mencionadas por Barmar y Joni, he notado que a veces tengo que dejar una línea en blanco antes y después de mi EOF cuando lo uso <<-EOF.

YOLO ROFL
fuente
1
No encuentro apoyo para esto ni en la teoría ni en la práctica. Votar en contra como superstición.
tripleee
1
Tenía un here-doc de catvarias líneas envuelto en parens para redirigir y debido al formato, justo antes de cerrar el marcador here-doc, necesitaba agregar una entrada antes de los parientes de cierre, de lo contrario, obtendría esta falta de coincidencia. La votación a favor es completamente válida para "algunas" personas, aunque probablemente sea un escenario poco probable.
SidOfc
Odio contribuir a la superstición, pero también estaba teniendo este problema y agregué una línea en blanco después del final de mi EOF. (ubuntu 19.10 ejecutándose en la ventana acoplable; esto estaba en un script bootstrap.sh). La línea en blanco solucionó este error.
NDP