Почему -a в «#! / Bin / sh -a» влияет на sed, а «set -a» - нет?

20

Если я запускаю следующий файл .sh:

#!/bin/sh -a
echo "a" | sed -e 's/[\d001-\d008]//g'

Результатом является ошибка:

sed: -e выражение # 1, символ 18: недопустимый конец диапазона

Но если я запускаю следующий файл .sh:

#!/bin/sh
set -a
echo "a" | sed -e 's/[\d001-\d008]//g'

Работает без ошибок. Разве второй код не должен быть эквивалентным первому? Почему ошибка в первом?

Родриго
источник
Не все shодинаковы. Не все sed эквивалентны. Что shвы используете? В какой ОС? и какой сед (возможно? sed --versionесли не подведет)?
Исаак
1
установка LC_COLLATE=C(или POSIX) для звонка, чтобы sedобойти проблему
Джефф Шаллер
4
Я обнаружил одно отличие: первый скрипт вызывает sed (и, вероятно, любую другую утилиту) POSIXLY_CORRECT=yв среде, второй не имеет POSIXLY_CORRECTв среде. Оболочка, из которой я вызываю оба сценария, отсутствует POSIXLY_CORRECTв ее среде.
Марк Плотник
1
Ах, echo "a" | POSIXLY_CORRECT=y sed -e 's/[\d001-\d008]//g' воспроизведите вашу проблему
Исаак
1
Подтверждение того, что вышеперечисленное не работает для меня, точно так же, как OP показал на CentOS 7.x - GNU bash, версия 4.2.46 (2) -релиз (x86_64-redhat-linux-gnu) и CentOS Linux выпуск 7.5.1804 (Core) ,
SLM

Ответы:

31

Когда bash вызывается с именем sh, он делает это :

if (shell_name[0] == 's' && shell_name[1] == 'h' && shell_name[2] == '\0')
    act_like_sh++;

а затем позже устанавливает POSIXLY_CORRECTпеременную оболочкиy :

if (act_like_sh)
  {
    bind_variable ("POSIXLY_CORRECT", "y", 0);
    sv_strict_posix ("POSIXLY_CORRECT");
  }

bind_variableвызовы bind_variable_internal, которые, если в это время включен атрибут оболочки a(что было бы, если бы вы вызвали оболочку -a), помечают переменную оболочки как экспортированную .

Итак, в вашем первом сценарии:

#!/bin/sh -a
echo "a" | sed -e 's/[\d001-\d008]//g'

sedвызывается POSIXLY_CORRECT=yв его среде, что заставит его жаловаться [\d001-\d008]. (То же самое происходит, если sed предоставлена --posixопция.)

В ГНУ СЭД, является кодом выхода для символа которого численное значение в базе-10 NNN , но в режиме POSIX, это отключено внутри выражения кронштейна, так что , буквально означает символы , и т.д., при этом диапазон от к . В порядке кодов символов, предшествует (и диапазон включает все цифры, кроме нуля, плюс все заглавные буквы, а также некоторые специальные символы). В используемой вами локали сортирует , однако, поэтому диапазон недопустим.\dNNN[\d001-\d008]\d1\1\en_US.UTF-8\1

В вашем втором сценарии:

#!/bin/sh
set -a
echo "a" | sed -e 's/[\d001-\d008]//g'

Несмотря на то, что POSIXLY_CORRECTон установлен в оболочке, он не экспортируется, поэтому sed вызывается без использования POSIXLY_CORRECTв среде, а sed запускается с расширениями GNU.

Если вы добавите export POSIXLY_CORRECTв верхней части вашего второго скрипта, вы также увидите жалобу sed.

Марк Плотник
источник
6
Для меня это ошибка.
Стефан Шазелас
1
святой панцирь ужаса, бэтмен! Это интересная причуда (и немного изменения, чтобы увидеть проблему, которая возникает из-за того, что ты на /bin/shсамом деле Bash). То же самое происходит, если находится в среде до запуска Bash: он также передает его как . POSIXLY_CORRECTshPOSIXLY_CORRECT=y
ilkkachu
3
@StevenPenny, но POSIXLY_CORRECT не в среде, когда запускается оболочка, и сценарий не устанавливает ее. Оболочка делает. Он создает переменную среды из ниоткуда, что очень плохо, поскольку он делает это в режиме, в котором он должен быть, и пытается соответствовать стандарту.
ilkkachu
4
FWIW, Bash также, кажется, не документирует, что это установило бы POSIXLY_CORRECTсамостоятельно. Там нет упоминания об этом в списке эффектов режима POSIX, а описание переменной говорит только о том, что установка этого параметра переводит оболочку в режим POSIX, а не наоборот.
ilkkachu
1
@ilkkachu. Выполнено. Я думаю, что спецификацию POSIX также следует обновить, чтобы уточнить, на какие переменные влияют allexport.
Стефан Шазелас