/usr/xpg4/bin/grep -q [^0-9] не всегда работает должным образом

У меня есть сценарий 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 числовых символов, как и должно быть. Эта ошибка кажется случайной, потому что она возникает только с некоторыми запросами. Однако, хотя отклоненные запросы постоянно возвращаются, одни и те же идентификаторы постоянно выбираются как недействительные день за днем.

Я сделал следующее:

  1. Машина Unix работает уже почти год, поэтому может потребоваться обновление. Системный администратор перезагрузил машину по моей просьбе. Но проблема сохраняется после перезагрузки.
  2. Я вручную запустил точно такие же два теста в функции, в командной строке, и все идентификаторы, которые были признаны недействительными ночью, действительны.
  3. Я знаю, что одни и те же команды могут вести себя по-разному при вызове вручную или в сценарии. Чтобы увидеть, как функция ведет себя в сценарии, приведенный выше отрывок кода представляет собой небольшой сценарий, который я запустил, чтобы воспроизвести проблему. И действительно, некоторые (хотя и не все) идентификаторы, признанные недействительными ночью, также признаются недействительными с помощью небольшого сценария устранения неполадок.
  4. Затем я изменил этот сценарий устранения неполадок, чтобы запускать два теста по одному, и обнаружил, что именно тест /usr/xpg4/bin/grep -q [^0-9] ошибочно находит некоторые идентификаторы как содержащие нечисловые символы. Ну, идентификаторы - это все числовые символы, по крайней мере, визуально.
  5. Я проверил, нет ли проблем с командным файлом xpg4 grep (ls -l /usr/xpg4/bin/grep), чтобы убедиться, что он добавлен туда недавно. Но его отметка времени - 2005 год (эта машина работает под управлением Solaris 10).
  6. Зная, что данные поступают из центральной ERP-системы, ввод данных в которую осуществляется из разных мест с использованием всевозможных терминальных машин, работающих под управлением всевозможных операционных систем, поддерживающих различные наборы символов и кодировки. Система ERP просто позволяет им это делать. Но могут ли символы из других кодировок визуально отображаться как числовые символы, но закодированные значения не такие, как ожидает команда /usr/xpg4/bin/grep на нашей машине Unix? Я попробовал команду od (восьмеричный дамп), но она мне не очень помогает, так как я с ней не знаком. Может быть, мне нужно больше узнать о od для решения этой проблемы.

Мой временный обходной путь — пропуск теста /usr/xpg4/bin/grep -q [^0-9]. Но проблема не решена. Что я могу попробовать дальше?

Пожалуйста, дайте нам фактический пример идентификатора, который неправильно обрабатывается. Кроме этого, включите set -x, и, возможно, вывод отладки покажет, что не так.

Jens 18.12.2020 20:20

PS: grep -q [^0-9] должно быть grep -q '[^0-9]'. Если у вас есть в CWD файл с именем a (или любое нечисловое имя из одной буквы), вы будете удивлены из-за подстановки оболочки. set -x, предложенный выше, сразу поймает это.

Jens 18.12.2020 20:25

При неудачном запуске od -c <<< "${1}", чтобы увидеть, есть ли в строке какие-либо нечисловые символы (конечный \n - это нормально)

markp-fuso 18.12.2020 20:37
Почему в Python есть оператор &quot;pass&quot;?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
3
360
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

newman 21.12.2020 20:53

В любом случае, когда проблема будет обнаружена, я верну обратно пропущенный /usr/xpg4/bin/grep в функцию и дам ей поработать некоторое время, чтобы доказать, что выражение без кавычек было причиной ошибки. А затем я заменю функцию второй предоставленной вами версией, которая проверяет две вещи за один шаг без использования внешнего командного файла.

newman 21.12.2020 20:53

@newman Приятно слышать, что это решило твою проблему. Ваша стратегия замены кода, безусловно, верна. Следующий шаг в Stackoverflow — принять ответ, который помог вам лучше всего, щелкнув галочку рядом с подсчетом голосов. Из любопытства, какое односимвольное имя файла в пользовательском каталоге задания cron?

Jens 21.12.2020 21:15

Здесь не используется односимвольное имя файла. Имя скрипта с проблемной функцией не односимвольное. Его нет в crontab, но он вызывается другим скриптом в crontab. Имя вызывающего скрипта тоже не односимвольное. Я знаю о разнице между регулярным выражением и глобированием, которое можно легко перепутать и запутать. Но я не знаю, как это было бы драматично с односимвольным именем файла.

newman 21.12.2020 22:21

@newman Уровень вложенности скриптов, вызывающих скрипты, не имеет значения. Вы можете продемонстрировать проблему с помощью echo [^0-9]. Вы ожидаете, что это выведет [^0-9], верно? Теперь touch a; echo [^0-9]. Это выводит a. Таким образом, используя шаблон grep, вы можете видеть, что вместо цифры он ищет букву a. Это явно не то, что вы ожидаете от своего сценария.

Jens 21.12.2020 22:56

Спасибо, Йенс, что поделился. Я пользователь ksh, не знакомый с bash. Я попробовал и оказалось, что вы описали поведение bash, а не ksh. В bash echo [^0-9] возвращает односимвольные имена файлов; echo m* возвращает все имена файлов, которые начинаются с m; echo [^0-9]* возвращает все имена файлов, которые начинаются с нечислового символа. Интересно, что в bash echo также выполняет работу ls.

newman 23.12.2020 19:49

Другие вопросы по теме