Shell-скрипт не дает ожидаемого результата в Ubuntu

avatar
sophist_pt
8 апреля 2018 в 06:09
74
1
4

Сценарий оболочки:

#!/bin/sh
# -*-sh-*-
java -classpath Test.jar Test test1.xml > javaOutput 2>&1;
if cat javaOutput | tr '\n' ' ' | grep ".*java.lang.IndexOutOfBoundsException0.*ArrayList.java:653.*Test.java:142.*" &>/dev/null; then
    echo TRUE;
else
    echo FALSE;
fi

Содержимое выходного файла (javaOutput):

0,2,468.000000
1,2,305.000000
2,5,2702.000000
3,3,1672.000000
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
    at java.util.ArrayList.rangeCheck(ArrayList.java:653)
    at java.util.ArrayList.get(ArrayList.java:429)
    at Test.processPayments(Test.java:113)
    at Test.processFile(Test.java:131)
    at Test.main(Test.java:142)

Я использую следующую версию Ubuntu:

Distributor ID: Ubuntu
Description:    Ubuntu 17.04
Release:    17.04

Когда я копирую и вставляю сценарий в командную строку, он работает нормально и выводит ЛОЖЬ, как и ожидалось, но всякий раз, когда я выполняю сценарий, он продолжает выводить ИСТИНА. Я даже запустил этот скрипт в Mac OS, и он работает, как и ожидалось, в MacOs. Я очень озадачен. Будем признательны за любую помощь или понимание.

Примечание: Здесь ожидается значение FALSE, поскольку в выходном содержимом нет строки IndexOutOfBoundsException0.

Источник
Gordon Davisson
8 апреля 2018 в 06:33
1

Если вы удалите &>/dev/null из сценария, что он напечатает? Кроме того, последние версии Ubuntu используют тире как /bin/sh (вместо более распространенного bash). Я не вижу ничего, что dash будет обрабатывать иначе, чем bash, но попробуйте установить shebang на #!/bin/bash и посмотреть, изменит ли поведение.

sophist_pt
8 апреля 2018 в 06:41
0

@GordonDavisson хороший звонок. Я удалил &>/dev/null, и он начал работать, однако для случаев, когда он повторяет TRUE, теперь он печатает вывод grep. Требование к сценарию состоит в том, чтобы не генерировать вывод, когда он оценивается как истинный. Знаете ли вы, есть ли другой способ достичь этого?

Gordon Davisson
8 апреля 2018 в 06:46
1

Я только что понял, почему это не работает. Приходит ответ...

Wildcard
14 июня 2019 в 20:08
0

Еще один вопрос, который может представлять интерес: Есть ли какой-либо код sh, который не является допустимым кодом bash?

Ответы (1)

avatar
Gordon Davisson
8 апреля 2018 в 06:55
4

Я понял это; это небольшая разница в синтаксисе между bash (программа-оболочка с множеством функций, добавленных поверх базового набора, указанного стандартом POSIX) и dash (минимальная оболочка POSIX). Многие ОС используют bash как /bin/sh, но Debian (и Ubuntu) некоторое время назад перешли на тире.

Синтаксическая разница в том, что сокращение &> для перенаправления стандартного вывода и стандартной ошибки в одно и то же место является расширением bash; dash будет анализировать это как два отдельных токена: & (разделитель команд, указывающий на запуск команды в фоновом режиме) и > (перенаправление стандартного вывода). Поскольку & отмечает конец одной команды и начало другой, перенаправление не применяется к предыдущей команде, это своего рода минимальная собственная команда. По сути, тире анализирует вещь между if и then как:

cat javaOutput | tr '\n' ' ' | grep ".*java.lang.IndexOutOfBoundsException0.*ArrayList.java:653.*Test.java:142.*" &
>/dev/null

... и if смотрит только на успех/неуспех последней команды в этом блоке, которая является просто перенаправлением... которое всегда завершается успешно.

К счастью, решение простое: не используйте сокращение bash, используйте полный 2>&1 для перенаправления как stdout, так и stderr:

if cat javaOutput | tr '\n' ' ' | grep ".*java.lang.IndexOutOfBoundsException0.*ArrayList.java:653.*Test.java:142.*" >/dev/null 2>&1; then
sophist_pt
8 апреля 2018 в 07:02
0

Большое спасибо. Это сработало отлично. Полезно знать настоящую причину.