Несогласованная синтаксическая ошибка в арифметическом выражении bash: (маркер ошибки «integer1-integer2»)

#bash #shell

#bash #оболочка

Вопрос:

Я написал скрипт для подмножества некоторых больших плоских файлов с целым числом подмножеств (т. Е. $ increment) в качестве переменной пользовательского ввода. Я наблюдаю странное поведение (синтаксическая ошибка bash) только тогда, когда этот входной аргумент является нечетным целым числом. С целью (незначительного) улучшения ясности я воспроизвел это поведение ошибки в урезанной версии моего исходного сценария оболочки. Я не могу предоставить плоские файлы, но, надеюсь, кто-нибудь с опытом работы с оболочкой / bash сможет диагностировать, что здесь происходит, просто просмотрев код и сообщения об ошибках (я уже прогнал этоhttp://www.shellcheck.net / и не обнаружил никаких серьезных проблем).

Когда значение $increment равно четному целому числу (например, 8), сценарий оболочки выполняется без ошибок и выводит нужные операторы печати (см. «ПРИМЕЧАНИЕ» ниже) для каждой итерации цикла while. Вот некоторые примеры вывода из этих операторов печати:

 Line of interest: span2=84688
Line of interest: span2=85225
Line of interest: span2=86323
...
  

Однако, когда $ increment является нечетным (например, 9), сценарий завершается ошибкой в строке 48 «span2 = $(($line2-$last2))» с инструкцией об ошибке:

 test_case.sh: line 48: 153026
153027-77419: syntax error in expression (error token is "153027-77419")
  

Это странно, потому что предыдущий вывод инструкции echo print «Строка интереса: span2 = 75278» указывает, что арифметическое выражение вычисляется в подоболочке без ошибок, непосредственно перед строкой с ошибкой. Таким образом, очевидно, что в целых числах, которые здесь вычитаются, нет ничего особенного, но странно, что сообщение об ошибке, например, выглядит как одно за другим, когда оно выводит «153026», когда аргумент выражения $ line2 равен «153027». Однако я не уверен, связано ли это с синтаксической ошибкой.

 #!/bin/bash
set -e


increment=9
file1="path/to/file1"
file2="path/to/file2"
file3="path/to/file3"

# End index of header in first file
file1_start=2138
midpoint=$(( $file1_start   1 ))

file1_wc=($(wc $file1))
file2_wc=($(wc $file2))
file3_wc=($(wc $file3))

# Get a line count for the three different flat text files, as an upper bound index
ceil1=${file1_wc[0]}
ceil2=${file2_wc[0]}
ceil3=${file3_wc[0]}

# Initialize end point indices
line="$(head -$midpoint $file1 | tail -1 | awk '{print $1;}')"
line2=$(grep -n -e "$line" $file2 | cut -f1 -d:)
line3=$(grep -n -e "$line" $file3 | cut -f1 -d:)

# Initialize starting point indices
last1=$midpoint
last2=$line2
last3=$line3

# Update "midpoint" index
midpoint=$(($midpoint $ceil1/$increment))

while [ $midpoint -lt $ceil1 ]
do

 line="$(head -$midpoint $file1 | tail -1 | awk '{print $1;}')"
 line2=$(grep -n -e "$line" $file2 | cut -f1 -d:)
 line3=$(grep -n -e "$line" $file3 | cut -f1 -d:)

 # Calculate range of indices for subset number $increment
 span1=$(($midpoint-$last1))

 echo "Line of interest: span2=$(($line2-$last2))"
 # ***NOTE***: The below statement is where it is failing for odd $increment
 span2=$(($line2-$last2))

 span3=$(($line3-$last3))

 # Set index variables for next iteration of file traversal
 index=$(($index 1))
 last1=$midpoint
 last2=$line2
 last3=$line3

 # Increment midpoint index variable
 midpoint=$(($midpoint $ceil1/$increment))

done
  

Мы высоко ценим ваши отзывы, заранее спасибо.


ОБНОВЛЕНИЕ: добавив «set -x» и просмотрев стек вызовов, я определил, что выражение

  line2=$(grep -n -e "$line" $file2 | cut -f1 -d:)
  

было скопировано более одной строки. Таким образом, в примере, который я привел выше, $ line2 был равен «153026 n153027» и не был надежным аргументом для вычитания, отсюда синтаксическая ошибка. Способ решить эту проблему — передать в head, например

  line2=$(grep -n -e "$line" $file2 | cut -f1 -d: | head -1)
  

рассматривать только первую строку, выданную grep.

Комментарии:

1. Вы пробовали вставлять set -x в скрипт, чтобы вы могли наблюдать за его выполнением? Это обычный способ отладки сценариев оболочки.

2. проверьте, нет ли непечатаемых символов в неверной строке

3. Убедитесь, что вы запускаете его с помощью bash: bash your_script.sh . Можете ли вы показать свою версию bash? bash --version

4. dos2unix myScriptName ? Исправляет 1/2 загадочных вопросов здесь, в S.O. 🙂 . Удачи.

5. Вы можете ответить на свой собственный вопрос. Я просто дал вам подсказки по отладке, я не нашел решения.

Ответ №1:

ncemami: добавив «set -x» и просмотрев стек вызовов, я определил, что выражение

  line2=$(grep -n -e "$line" $file2 | cut -f1 -d:)
  

было скопировано более одной строки. Таким образом, в примере, который я привел выше, $ line2 был равен «153026 n153027» и не был надежным аргументом для вычитания, отсюда синтаксическая ошибка. Способ решить эту проблему — передать в head, например

  line2=$(grep -n -e "$line" $file2 | cut -f1 -d: | head -1)
  

рассматривать только первую строку, выданную grep.

Комментарии:

1. Спасибо! Ваш ответ заставил меня понять, что одно из моих значений содержит дополнительный пробел, который не подходит для сравнения.