Почему мой сценарий Bash не возвращает правильный ответ этому Project Euler?

547
Egrodo

Я пытаюсь использовать Bash для завершения Project Euler 13 . Ниже приведен мой код, с которым я просто не могу понять, в чем дело.

#!/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 "$"  exit  

Раньше это приводило к двум ошибкам,

(standard_in) 1: syntax error 

а также

(standard_in) 1: illegal character: ^M 

После некоторых исследований это, похоже, стало проблемой с терминаторами EOF. Затем я запустил на нем dos2unix, и он больше не выдает вторую ошибку, но все равно выдает первую повторно. Кажется, есть некоторая проблема с тем, как я передаю данные в bc, но я понятия не имею, что или как это исправить.

Правильный ответ 5537376230. Большое спасибо за все, что вы можете помочь с!

Информация о системе

GNU bash, версия 4.3.11 (1) -релиз (x86_64-pc-linux-gnu)

Я использую cmder на Windows 10.

1
Он не выдавал никаких ошибок в оболочке Ubuntu LTS. Он отвечает `5483872696` ... Hastur 7 лет назад 1
Извините, должен был указать, правильный ответ 5537376230. Egrodo 7 лет назад 0
Да, вы должны (нет проблем, это только помешало мне и, возможно, другим пойти дальше), но внутри есть другая информация ... она не вызвала никаких ошибок. Пожалуйста, для полноты добавьте версию ОС, на которой вы запускаете скрипт (дистрибутив, выпуск, версия bash ...). Это хорошая привычка в целом и может помочь понять. Hastur 7 лет назад 0
Хорошо, извини, я обязательно сделаю это в будущем. Спасибо за совет. Egrodo 7 лет назад 0
Не о чем сожалеть ... `:-)`. Кстати, это всего лишь вопрос нескольких секунд, действительно добавьте ОС и версию вашей системы. Мне все еще любопытно узнать причину, которая вызывает "синтаксическую ошибку". Помещение кода в скрипт в моей системе не выдает никакой ошибки ... Hastur 7 лет назад 0
Просто добавил мою системную информацию! Egrodo 7 лет назад 0

2 ответа на вопрос

3
Spiff

Вы хотите установить в качестве readразделителя \ n (== 0x0a == LF == перевод строки), а не \r(== 0x0d == CR == возврат каретки). Либо так, либо обязательно поставьте CR в конце вашего файла pastebin. Похоже, что в вашем файле pastebin отсутствует последовательность конца строки в конце последней строки, поэтому последняя строка никогда не передается в ваш скрипт.

$ 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 

Обратите внимание, что между каждым числом есть 0x0d0a (CR LF), но не после последнего.

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

Обратите внимание, что последний номер, 535 [...] 690, отсутствует при запуске через вашу readкоманду. Но если вы переключите свой разделитель на символ новой строки LF (\ n) Unix, включится последняя строка:

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

Отредактировано, чтобы добавить: Вот исправление, которое обрабатывает CR в файле pastebin. Я сказал readиспользовать CRLF в качестве разделителя, и использовал дополнительное эхо для добавления CRLF после файла 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 "$" 
О боже, очевидно, Пастебин на концах отбрасывает терминатор, как бы я его ни вставил, это странно. Вместо этого я жестко закодировал числа в своем сценарии и сохранил разделитель CR, как вы можете видеть здесь: http://pastebin.com/raw/g2iDchun. Но по какой-то причине он возвращает только «0» :( Когда я изменяю его на разделитель LF \ n, он возвращает вместо «(standard_in) 2: синтаксическая ошибка». Любые идеи, почему это может происходить? Спасибо за вашу помощь в очистке Различия между разрывами строк! Egrodo 7 лет назад 0
Ах, `bc` задыхается, потому что в твоей пасте есть CR. Когда я проверял свое изменение, я проверял его с простым `echo`, не добавляя его в` bc`. Что касается вашей новой проблемы, то оболочка не сохраняет ваши новые строки в этом строковом присваивании, поэтому `numbers" в конечном итоге представляет собой одну длинную строку с пробелами между числами. Spiff 7 лет назад 1
@Egrodo Хорошо, я обновил свой ответ реальным, полностью протестированным решением. Spiff 7 лет назад 1
я вижу, я вижу, я не осознавал, что оболочка не сохранила разрывы строк, это довольно глупо. Ваше новое решение работает отлично, большое спасибо за то время, которое вы потратили, помогая мне с этим. Если вы не возражаете, я пытаюсь выяснить, что именно происходит с тем, что вводится в цикл while. Итак, я стандартно - ввод содержимого пастбина, а затем ввод эха? Afaik, выполняя готовую <<< вещь, просто говорит «дать этот ввод всему, что в цикле запрашивает ввод». Как эхо играет в этом? Egrodo 7 лет назад 0
Да, вещь `command <<<" строка "` называется "здесь строкой", и это примерно то же самое, что использование `echo" строки "| command`. В вашем случае вы используете `$ (curl…; echo…)` для формирования «строки здесь» из стандартного вывода `curl`, за которым следует стандартный вывод` echo`. В сценариях оболочки точка с запятой разделяет команды, позволяя вам поместить несколько команд в одну строку. Spiff 7 лет назад 1
3
glenn jackman

Вы можете сделать это:

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

Что использует:

  • замена процесс содержит вызов к скручиванию и трубы в СЭД, чтобы удалить возврат каретки.
  • перенаправить это в mapfile, чтобы прочитать строки ввода в массив оболочки
  • перебрать массив и вызвать bc с выражением, переданным здесь