¿Por qué mi script Bash no devuelve la respuesta correcta a este Proyecto Euler?

1

Estoy tratando de usar Bash para completar el Proyecto Euler 13 . A continuación se muestra mi código que simplemente no puedo entender qué está mal.

#!/bin/bash                                                
sum=0                                                      
while read -r -d $'\r' line; do                            
    sum=$(echo $sum + $line | bc)                          
done <<< "$(curl -s http://pastebin.com/raw/uHZ0PZjm)"     
echo "${sum:0:10}"                                         
exit 

Solía ​​dar lugar a dos errores,

(standard_in) 1: syntax error

y

(standard_in) 1: illegal character: ^M

Después de algunas investigaciones, parecía ser un problema con los terminadores EOF. Luego ejecuté dos2unix en él y ya no da el segundo error, pero sigue dando el primero repetidamente. Parece haber algún problema con la forma en que estoy canalizando los datos en bc, pero no tengo idea de qué o cómo solucionarlo.

La respuesta correcta es 5537376230. ¡Muchas gracias por todo lo que pueda ayudar!

La información del sistema es

GNU bash, versión 4.3.11 (1) -release (x86_64-pc-linux-gnu)

Estoy usando cmder en Windows 10.

Egrodo
fuente
1
No arrojó ningún error en un shell bash de Ubuntu LTS. Responde 5483872696...
Hastur
Lo sentimos, debería haber especificado, la respuesta correcta es 5537376230.
Egrodo
Sí, debería (no hay problema, esto solo me impidió a mí y tal vez a otros ir más allá), pero hay otra información dentro ... no generó ningún error. Agregue para completar la versión del sistema operativo en la que está ejecutando el script (distribución, lanzamiento, versión de bash ...). Es un buen hábito en general y puede ayudar a entender.
Hastur
Bien, lo siento, me aseguraré de hacerlo en el futuro. Gracias por el consejo.
Egrodo
Nada que lamentar ... :-). Por cierto, solo es cuestión de unos segundos, realmente agregue el sistema operativo y la versión de su sistema. Todavía tengo curiosidad por saber la razón que causa la syntax error. Poner el código en un script en mi sistema no arroja ningún error ...
Hastur

Respuestas:

3

Desea establecer \ n (== 0x0a == LF == salto de línea) como su readdelimitador, no \r(== 0x0d == CR == retorno de carro). O eso, o asegúrese de poner un CR al final de su archivo pastebin. Parece que al archivo pastebin le falta una secuencia de terminación de línea al final de la última línea, por lo que la última línea nunca se alimenta a su script.

$ curl -s http://pastebin.com/raw/uHZ0PZjm | hexdump -C | tail -n 8
...(snip)...
000013e0  35 30 39 35 31 36 0d 0a  32 30 38 34 39 36 30 33  |509516..20849603|
000013f0  39 38 30 31 33 34 30 30  31 37 32 33 39 33 30 36  |9801340017239306|
00001400  37 31 36 36 36 38 32 33  35 35 35 32 34 35 32 35  |7166682355524525|
00001410  32 38 30 34 36 30 39 37  32 32 0d 0a 35 33 35 30  |2804609722..5350|
00001420  33 35 33 34 32 32 36 34  37 32 35 32 34 32 35 30  |3534226472524250|
00001430  38 37 34 30 35 34 30 37  35 35 39 31 37 38 39 37  |8740540755917897|
00001440  38 31 32 36 34 33 33 30  33 33 31 36 39 30        |81264330331690|
0000144e

Tenga en cuenta que hay un 0x0d0a (CR LF) entre cada número, pero no después del último.

$ while read -r -d $'\r' line; do echo $line; done <<< "$(curl -s http://pastebin.com/raw/uHZ0PZjm)" | tail -n 3
77158542502016545090413245809786882778948721859617
72107838435069186155435662884062257473692284509516
20849603980134001723930671666823555245252804609722

Tenga en cuenta que el último número, 535 [...] 690, falta cuando se ejecuta a través de su readcomando. Pero si cambia su delimitador al carácter de línea nueva LF (\ n) nativo de Unix, se incluye la última línea:

$ while read -r -d $'\n' line; do echo $line; done <<< "$(curl -s http://pastebin.com/raw/uHZ0PZjm)" | tail -n 3
72107838435069186155435662884062257473692284509516
20849603980134001723930671666823555245252804609722
53503534226472524250874054075591789781264330331690

Editado para agregar: Aquí hay una solución que maneja los CR en el archivo pastebin. Le dije readque usara CRLF como delimitador, y usé un eco adicional para agregar un CRLF después del archivo pastebin.

sum=0
while read -r -d $'\r\n' line; do
    sum=$(echo $sum + $line | bc)
done <<< $(curl -s http://pastebin.com/raw/uHZ0PZjm; echo -e "\r\n")
echo "${sum:0:10}"
Spiff
fuente
Oh, Dios, evidentemente Pastebin desecha el terminador al final, no importa cómo lo pegue, eso es un problema. En cambio, codifiqué los números en mi script y mantuve el delimitador CR como puedes ver aquí: pastebin.com/raw/g2iDchun . Pero por alguna razón que devuelve solo "0" :( Cuando lo cambio al delimitador LF \ n devuelve en su lugar "(standard_in) 2: error de sintaxis". ¿Alguna idea de por qué podría estar sucediendo esto? Gracias por su ayuda para aclarar diferencias entre los saltos de línea!
Egrodo
1
Ah, se bcestá asfixiando porque tu pastebin tiene CR's. Cuando probé mi cambio, lo probé con un plano echo, sin conectarlo bc. En cuanto a su nuevo problema, el shell no conserva sus nuevas líneas en esa asignación de cadena, por lo que $numberstermina siendo una línea larga con espacios entre los números.
Spiff
1
@Egrodo Bien, actualicé mi respuesta con una solución real y completamente probada.
Spiff
Ya veo, no me di cuenta de que el caparazón no conservaba los saltos de línea, eso es bastante tonto. Su nueva solución funciona perfectamente, muchas gracias por el tiempo que ha dedicado a ayudarme con esto. Si no le importa, estoy tratando de averiguar exactamente qué sucede con las cosas que se ingresan en el bucle while. Entonces, ¿estoy normalizando el contenido del pastebin y luego también ingresando el eco? Afaik haciendo lo hecho <<< solo dice "dale esta entrada a cualquier cosa en el ciclo que pida entrada". ¿Cómo juega eco en eso?
Egrodo
1
Sí, la command <<< "string"cosa se llama "cadena aquí", y es casi lo mismo que usar echo "string" | command. En su caso, está utilizando $(curl… ; echo…)para formar la "cadena aquí" a partir de la salida estándar de curl, seguida de la salida estándar de echo. En las secuencias de comandos de shell, un punto y coma separa los comandos, lo que le permite colocar varios comandos en una línea.
Spiff
3

Puedes hacerlo:

mapfile -t lines < <(curl -s http://pastebin.com/raw/uHZ0PZjm | sed 's/\r$//')
sum=0
for bignum in "${lines[@]}"; do 
    sum=$(bc <<< "$sum + $bignum")
done
echo "${sum:0:10}"    # ==> 5537376230

Que usa:

  • Una sustitución de proceso para contener la llamada a curvatura y tubería en sed para eliminar los retornos de carro.
  • redirigir eso al mapfile para leer las líneas de la entrada en una matriz de shell
  • iterar sobre la matriz y llamar a bc con la expresión pasada con una cadena aquí
Glenn Jackman
fuente