Por que meu script Bash não está retornando a resposta correta para este Project Euler?

1

Estou tentando usar o Bash para concluir o Projeto Euler 13 . Abaixo está o meu código que eu simplesmente não consigo descobrir o que está errado.

#!/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 

Isso costumava resultar em dois erros,

(standard_in) 1: syntax error

e

(standard_in) 1: illegal character: ^M

Após algumas pesquisas, pareceu ser um problema com os terminadores de EOF. Eu então executei o dos2unix nele e ele não fornece mais o segundo erro, mas ainda está dando o primeiro repetidamente. Parece haver algum problema com a maneira como estou colocando os dados em bc, mas não tenho ideia de como corrigi-los.

A resposta correta é 5537376230. Muito obrigado por qualquer coisa que você possa ajudar!

Informações do sistema são

GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)

Estou usando o cmder no Windows 10.

    
por Egrodo 16.02.2017 / 01:02

2 respostas

3

Você deseja definir \ n (== 0x0a == LF == linefeed) como seu delimitador read , não \r (== 0x0d == CR == retorno de carro). Ou isso, ou certifique-se de colocar um CR no final do seu arquivo pastebin. Parece que seu arquivo pastebin está faltando uma sequência de terminador de linha no final da última linha, então a última linha nunca é inserida em seu 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

Note que há um 0x0d0a (CR LF) entre cada número, mas não depois do último.

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

Note que o último número, 535 [...] 690, está faltando quando executado através do comando read . Mas se você alternar seu delimitador para o caractere de nova linha LF (\ n) do Unix, a última linha será incluída:

$ 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 adicionar: Aqui está uma correção que lida com os CRs no arquivo pastebin. Eu disse a read para usar o CRLF como delimitador e usei um eco extra para adicionar um CRLF após o arquivo 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}"
    
por 16.02.2017 / 01:36
3

Você pode fazer isso:

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

Isso usa:

  • uma substituição de processo para conter a chamada para enrolar e canalizar para o sed para remover os retornos de carro.
  • redirecionar para mapfile para ler as linhas da entrada em um matriz de shell
  • iterar pelo array e chamar bc com a expressão passada com string aqui
por 16.02.2017 / 02:31