#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. Спасибо! Ваш ответ заставил меня понять, что одно из моих значений содержит дополнительный пробел, который не подходит для сравнения.