У меня есть сценарий Unix ksh, который используется ежедневно в течение многих лет (запускается ночью с помощью crontab). В последнее время одна функция в скрипте ведет себя хаотично, как никогда раньше. Пробовал разными способами выяснить причину, но безуспешно.
Функция проверяет входную строку, которая должна быть строкой из 10 числовых символов. Функция проверяет, равна ли длина строки 10 и содержит ли она какие-либо нечисловые символы:
#! /bin/ksh
# The function:
is_valid_id () {
# Takes one argument, which is the ID being tested.
if [[ $(print ${#1}) -ne 10 ]] || print "$1" | /usr/xpg4/bin/grep -q [^0-9] ; then
return 1
else
return 0
fi
}
cat $input_file | while read line ; do
id=$(print $line | awk -F: '{print $5}')
# Calling the function:
is_valid_id $id
stat=$?
if [[ $stat -eq 1 ]] ; then
print "The ID $id is invalid. Request rejected.\n" >> $ERRLOG
continue
else
...
fi
done
Проблема с функцией заключается в том, что каждую ночь из десятков или сотен запросов она находит идентификаторы в нескольких запросах недействительными. Я визуально проверил входные данные и обнаружил, что все «недопустимые» идентификаторы на самом деле являются строками из 10 числовых символов, как и должно быть. Эта ошибка кажется случайной, потому что она возникает только с некоторыми запросами. Однако, хотя отклоненные запросы постоянно возвращаются, одни и те же идентификаторы постоянно выбираются как недействительные день за днем.
Я сделал следующее:
/usr/xpg4/bin/grep -q [^0-9]
ошибочно находит некоторые идентификаторы как содержащие нечисловые символы. Ну, идентификаторы - это все числовые символы, по крайней мере, визуально.ls -l /usr/xpg4/bin/grep
), чтобы убедиться, что он добавлен туда недавно. Но его отметка времени - 2005 год (эта машина работает под управлением Solaris 10)./usr/xpg4/bin/grep
на нашей машине Unix? Я попробовал команду od
(восьмеричный дамп), но она мне не очень помогает, так как я с ней не знаком. Может быть, мне нужно больше узнать о od
для решения этой проблемы.Мой временный обходной путь — пропуск теста /usr/xpg4/bin/grep -q [^0-9]. Но проблема не решена. Что я могу попробовать дальше?
PS: grep -q [^0-9] должно быть grep -q '[^0-9]'. Если у вас есть в CWD файл с именем a (или любое нечисловое имя из одной буквы), вы будете удивлены из-за подстановки оболочки. set -x, предложенный выше, сразу поймает это.
При неудачном запуске od -c <<< "${1}", чтобы увидеть, есть ли в строке какие-либо нечисловые символы (конечный \n - это нормально)
Ваша функция проверки достоверности оказывается более сложной, чем должна быть. Например. почему вы используете замену команды с print на ${#1}? Почему бы вам не использовать ${#1} напрямую? Далее, разветвление grep для проверки не числа — медленная и дорогая операция. Как насчет этой эквивалентной функции, 100% POSIX и невероятно быстрой:
is_valid_id () {
# Takes one argument, which is the ID being tested.
if test ${#1} -ne 10; then
return 1 # ID length not exactly 10.
fi
case $1 in
(*[!0-9]*) return 1;; # ID contains a non-digit.
(*) return 0;; # ID is exactly 10 digits.
esac
}
Или еще проще, если вы не против повториться:
is_valid_id () {
# Takes one argument, which is the ID being tested.
case $1 in
([0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]) # 10 digits.
return 0;;
(*)
return 1;;
esac
}
Это также позволяет избежать использования шаблона grep без кавычек, который подвержен ошибкам при наличии односимвольных имен файлов. Это работает лучше?
Большое спасибо Йенс. Проблема оказывается в регулярном выражении, которое не заключено в кавычки. В тестовом сценарии после добавления кавычек к выражению [^0-9] проблема исчезла. Вызываемая функция выполняется в подоболочке, до которой, похоже, не доходит режим отладки set -x. Это только показывает, что функция вернула 1, не показывая, как функция выполняется в подоболочке.
В любом случае, когда проблема будет обнаружена, я верну обратно пропущенный /usr/xpg4/bin/grep в функцию и дам ей поработать некоторое время, чтобы доказать, что выражение без кавычек было причиной ошибки. А затем я заменю функцию второй предоставленной вами версией, которая проверяет две вещи за один шаг без использования внешнего командного файла.
@newman Приятно слышать, что это решило твою проблему. Ваша стратегия замены кода, безусловно, верна. Следующий шаг в Stackoverflow — принять ответ, который помог вам лучше всего, щелкнув галочку рядом с подсчетом голосов. Из любопытства, какое односимвольное имя файла в пользовательском каталоге задания cron?
Здесь не используется односимвольное имя файла. Имя скрипта с проблемной функцией не односимвольное. Его нет в crontab, но он вызывается другим скриптом в crontab. Имя вызывающего скрипта тоже не односимвольное. Я знаю о разнице между регулярным выражением и глобированием, которое можно легко перепутать и запутать. Но я не знаю, как это было бы драматично с односимвольным именем файла.
@newman Уровень вложенности скриптов, вызывающих скрипты, не имеет значения. Вы можете продемонстрировать проблему с помощью echo [^0-9]. Вы ожидаете, что это выведет [^0-9], верно? Теперь touch a; echo [^0-9]. Это выводит a. Таким образом, используя шаблон grep, вы можете видеть, что вместо цифры он ищет букву a. Это явно не то, что вы ожидаете от своего сценария.
Спасибо, Йенс, что поделился. Я пользователь ksh, не знакомый с bash. Я попробовал и оказалось, что вы описали поведение bash, а не ksh. В bash echo [^0-9] возвращает односимвольные имена файлов; echo m* возвращает все имена файлов, которые начинаются с m; echo [^0-9]* возвращает все имена файлов, которые начинаются с нечислового символа. Интересно, что в bash echo также выполняет работу ls.
Пожалуйста, дайте нам фактический пример идентификатора, который неправильно обрабатывается. Кроме этого, включите set -x, и, возможно, вывод отладки покажет, что не так.