Получить статус выхода процесса, который передается в другой

У меня есть два процесса foo и bar, связанные с каналом:

$ foo | бар

bar всегда выходит 0; Мне интересен код выхода foo. Есть ли способ добраться до него?

220 голосов | спросил Michael Mrozek 3 J0000006Europe/Moscow 2011, 00:19:35

15 ответов


184

Если вы используете bash, вы можете использовать переменную массива PIPESTATUS, чтобы получить статус выхода каждого элемента конвейера.

$ false | правда
$ echo "$ {PIPESTATUS [0]} $ {PIPESTATUS [1]}"
1 0

Если вы используете zsh, они называются pipestatus (дело имеет значение!), а индексы массива начинаются с одного:

$ false | правда
$ echo "$ {pipestatus [1]} $ {pipestatus [2]}"
1 0

Чтобы объединить их внутри функции таким образом, чтобы не потерять значения:

$ false | правда
$ retval_bash = "$ {PIPESTATUS [0]}" retval_zsh = "$ {pipestatus [1]}" retval_final = $?
$ echo $ retval_bash $ retval_zsh $ retval_final
1 0

Запустите выше в bash или zsh, и вы получите те же результаты; будет установлен только один из retval_bash и retval_zsh. Другой будет пустым. Это позволит функции закончить с помощью return $ retval_bash $ retval_zsh (обратите внимание на отсутствие кавычек!).

ответил camh 3 J0000006Europe/Moscow 2011, 00:49:45
193

Существует 3 общих способа:

Pipefail

Первый способ - установить опцию pipefail (ksh, zsh или bash)). Это самое простое и что он делает, в основном устанавливает статус выхода $? в код выхода последней программы для выхода из ненулевой (или нулевой, если все вышло успешно).

$ false | правда; echo $?
0
$ set -o pipefail
$ false | правда; echo $?
1

$ PIPESTATUS

Bash также имеет переменную массива, называемую $ PIPESTATUS ($ pipestatus в zsh)), которая содержит статус выхода всех программ в последний трубопровод.

$ true | правда; echo "$ {PIPESTATUS [@]}"
0 0
$ false | правда; echo "$ {PIPESTATUS [@]}"
1 0
$ false | правда; echo "$ {PIPESTATUS [0]}"
1
$ true | ложный; echo "$ {PIPESTATUS [@]}"
0 1

Вы можете использовать третий пример команды, чтобы получить конкретное значение в конвейере, в котором вы нуждаетесь.

Отдельные исполнения

Это самый громоздкий из решений. Запускайте каждую команду отдельно и фиксируйте статус

$ OUTPUT = "$ (echo foo)"
$ STATUS_ECHO = "$?"
$ printf '% s' "$ OUTPUT" | grep -iq "bar"
$ STATUS_GREP = "$?"
$ echo "$ STATUS_ECHO $ ​​STATUS_GREP"
0 1
ответил Patrick 21 PMpSun, 21 Apr 2013 17:56:36 +040056Sunday 2013, 17:56:36
34

В то время как не совсем то, что вы просили, вы можете использовать

#! /bin /bash -o pipefail

, чтобы ваши трубы возвращали последний ненулевой возврат.

может быть немного меньше кодирования

Изменить: Пример

[root @ localhost ~] # false | правда
[root @ localhost ~] # echo $?
0
[root @ localhost ~] # set -o pipefail
[root @ localhost ~] # false | правда
[root @ localhost ~] # echo $?
1
ответил Chris 3 J0000006Europe/Moscow 2011, 01:59:49
34

Это решение работает без использования специальных функций или временных файлов. Бонус: в конце концов статус выхода на самом деле является статусом выхода, а не некоторой строкой в ​​файле.

Ситуация:

someprog | фильтр

вы хотите получить статус выхода из someprog и вывод из filter.

Вот мое решение:

((((someprog; echo $?> & 3) | filter> & 4) 3 & 1) | (чтение xs; выход $ xs)) 4 & 1

echo $?

Пример someprog и filter:

someprog () {
  echo "line1"
  echo "line2"
  echo "line3"
  возвращение 42
}

filter () {
  при чтении строки; делать
    эхо "отфильтрованная $ строка"
  сделанный
}

((((someprog; echo $? & 3) | filter> 4) 3 & 1) | (чтение xs; выход $ xs)) 4 & 1

echo $?

Пример вывода:

отфильтрованная строка1
отфильтрованная линия2
отфильтрованная линия3
42

Примечание: дочерний процесс наследует дескрипторы открытого файла от родителя. Это означает, что someprog наследует открытый файловый дескриптор 3 и 4. Если someprog записывается в дескриптор файла 3, то это станет статусом выхода. Действительный статус выхода будет проигнорирован, потому что read читает только один раз.

Если вы опасаетесь, что ваш someprog может записать в дескриптор файла 3 или 4, лучше закрыть дескрипторы файла перед вызовом someprog.

((((exec 3 & - 4 & - someprog), echo $? & 3) | filter> 4) 3 & 1) | (чтение xs; выход $ xs)) 4 & 1

exec 3> &> & - перед someprog закрывает дескриптор файла перед выполнением someprog, поэтому для someprog эти файловые дескрипторы просто не существуют.


Пошаговое объяснение конструкции:

((((someprog; # part6
        echo $? & 3 # part5
      ) | фильтр> & 4 # part4
    ) 3 & 1 # part3
  ) | (чтение xs; выход $ xs) # part2
) 4 & 1 # part1

Снизу вверх:

  1. Создана подоболочка с файловым дескриптором 4, перенаправленным на stdout. Это означает, что все, что напечатано в файловом дескрипторе 4 в подоболочке, закончится как stdout всей конструкции.
  2. Создается труба и выполняются команды слева (# part3) и right (# part2). exit $ xs также является последней командой канала, и это означает, что строка из stdin будет статусом выхода всей конструкции.
  3. Создана подоболочка с файловым дескриптором 3, перенаправленным на stdout. Это означает, что все, что напечатано в дескрипторе файла 3 в этой подоболочке, закончится в # part2 и, в свою очередь, будет статусом выхода всей конструкции.
  4. Создается труба и команды слева (# part5 и # part6)) и правый (filter> & 4 ). Вывод фильтра перенаправляется в дескриптор файла 4. В # part1 файловый дескриптор 4 был перенаправлен на stdout. Это означает, что вывод filter - это stdout всей конструкции.
  5. Статус выхода из # part6 печатается в дескриптор файла 3. В # part3 дескриптор файла 3 был перенаправлен на # part2. Это означает, что статус завершения из # part6 будет конечным статусом выхода для всей конструкции.
  6. someprog. Статус выхода берется в # part5. Stdout берется трубой в # part4 и перенаправляется на filter. Выход из filter, в свою очередь, достигнет stdout, как описано в # part4
ответил lesmana 31 MarpmSun, 31 Mar 2013 14:08:17 +04002013-03-31T14:08:17+04:0002 2013, 14:08:17
22

Что я делаю, когда это возможно, это передать код выхода из foo в bar. Например, если я знаю, что foo никогда не создает строку с просто цифрами, тогда я могу просто нажать на код выхода:

{foo; echo "$?"; } | awk '! /[^ 0-9] /{exit ($ 0)} {â € |}'

Или, если я знаю, что вывод из foo никогда не содержит строку с просто .:

{foo; эхо.; echo "$?"; } | awk '/^\.$/{getline; exit ($ 0)} {â € |} '

Это всегда можно сделать, если есть способ заставить bar работать на всех, кроме последней строки, и передать последнюю строку в качестве кода выхода.

Если bar - сложный конвейер, выход которого вам не нужен, вы можете обойти его часть, распечатав код выхода в другом файловом дескрипторе.

exit_codes = $ ({{foo; echo foo: "$?"> & 3;} |
               {bar> /dev /null; эхо-бар: «$?» > & амп; 3; }
             } 3 & 1)

После этого $ exit_codes обычно foo: X bar: Y, но это может быть строка : Y foo: X, если bar завершает работу перед чтением всего своего ввода или если вам не повезло. Я думаю, что записи в каналы размером до 512 байт являются атомарными для всех униформ, поэтому части foo: $? и : $? не будут перемешаны до тех пор, пока строки тегов находятся под 507 байтами.

Если вам нужно захватить вывод из bar, становится сложно. Вы можете объединить вышеприведенные методы, указав, что вывод bar никогда не содержит строку, которая выглядит как индикация кода выхода, но она становится неудобной.

выход = $ (эхо;
         {{foo; echo foo: "$?" > & амп; 3; } |
           {bar | sed 's /^ /^ /'; эхо-бар: «$?» > & амп; 3; }
         } 3 & 1)
п =»
'
foo_exit_code = $ {выход # * $ {п} Foo:}; foo_exit_code = $ {foo_exit_code %% $ п *}
bar_exit_code = $ {выход # * $ {п} бар:}; bar_exit_code = $ {bar_exit_code %% $ п *}
output = $ (printf% s "$ output" | sed -n 's /^ \ ^ //p')

И, конечно, есть простой вариант с использованием временного файла для сохранения статуса. Простой, но не , который прост в производстве:

  • Если несколько сценариев работают одновременно или если один и тот же сценарий использует этот метод в нескольких местах, вам нужно убедиться, что они используют разные временные имена файлов.
  • Безопасное создание временного файла в общем каталоге затруднено. Часто /tmp - это единственное место, где скрипт обязательно сможет записывать файлы. Используйте mktemp , который не является POSIX, но доступен во всех серьезных организациях в настоящее время.
foo_ret_file = $ (mktemp -t)
{foo; эхо "$?" > "$ foo_ret_file"; } | бар
bar_ret = $?
foo_ret = $ (cat "$ foo_ret_file"; rm -f "$ foo_ret_file")
ответил Gilles 3 J0000006Europe/Moscow 2011, 04:33:16
17

Начиная с конвейера:

foo | бар | Baz

Вот общее решение, использующее только оболочку POSIX и без временных файлов:

exec 4 & 1
error_statuses = "` ((foo || echo "0: $?"> & 3) |
        (bar || echo "1: $?"> & 3) |
        (baz || echo "2: $?" & 3)) 3 & 1 & 4` "
exec 4 &

$ error_statuses содержит коды состояния любых неудачных процессов в случайном порядке с индексами, чтобы указать, какая команда выдала каждый статус.

# Если «bar» не удалось, выведите свой статус:
echo "$ error_statuses" | grep '1:' | cut -d: -f2

# если все команды преуспели:
test -z "$ error_statuses"

# если последняя команда выполнена успешно:
! echo "$ error_statuses" | grep '2:'> /dev /null

Обратите внимание на цитаты вокруг $ error_statuses в моих тестах; без них grep не может различаться, потому что символы новой строки принуждаются к пробелам.

ответил Jander 15 J000000Friday11 2011, 11:02:45
11

Поэтому я хотел внести свой вклад, например, lesmana's, но я считаю, что мой, возможно, немного проще и немного более выгодным чистым решением Bourne-shell:

# Вы хотите передать команду command1 через command2:
exec 4 & 1
exitstatus = `{{command1; printf $? 1 > & амп; 3; } | command2 1 & 4; } 3 & 1 & quot;
# $ exitstatus теперь имеет статус выхода команды command1.

Я думаю, что это лучше всего объясняется изнутри: команда 1 будет выполнять и печатать свой обычный вывод на stdout (дескриптор файла 1), а затем, как только это будет сделано, printf выполнит и распечатает код выхода command1 на его stdout, но что stdout перенаправляется на дескриптор файла 3.

В то время как command1 запущен, его stdout передается по команде в command2 (вывод printf никогда не приводит к команде2, потому что мы отправляем его в дескриптор файла 3 вместо 1, что и читает труба). Затем мы перенаправляем вывод command2 в дескриптор файла 4, чтобы он также оставался вне файлового дескриптора 1, потому что мы хотим, чтобы дескриптор файла 1 был освобожден немного позже, потому что мы будем выводить вывод printf на дескриптор файла 3 обратно в дескриптор файла 1 - потому что это то, что будет замещать команда (обратные шаги), и это то, что будет помещено в переменную.

Последний бит магии состоит в том, что первый exec 4> & 1 мы сделали как отдельную команду - он открывает файловый дескриптор 4 в качестве копии исходного файла внешней оболочки. Подстановка команд будет захватывать все, что написано на стандарте, с точки зрения команд внутри него - но, поскольку вывод команды 2 будет записывать дескриптор 4 в случае подстановки команды, подстановка команды не захватывает его. «Однако, как только он« выйдет »из подстановки команды, он по-прежнему будет продолжать общий файловый дескриптор сценария 1.

(exec 4> & 1 должен быть отдельной командой, потому что многим обычным оболочкам не нравится, когда вы пытаетесь записать в дескриптор файла внутри подстановки команд, которая открывается в «внешней» команде, использующей подстановку. Таким образом, это самый простой переносимый способ сделать это.)

Вы можете смотреть на него менее техничным и более игривым способом, как если бы выходы команд перескакивали друг с другом: command1 pipe to command2, то вывод printf перескакивает по команде 2, так что command2 не поймает его , а затем вывод команды 2 перевыполняется и вытесняется из подстановки команд так же, как printf приземляется как раз вовремя, чтобы получить захват подстановкой, так что он попадает в переменную, а вывод command2 продолжает свой веселый путь, записываемый на стандартный вывод , как и в обычной трубе.

Кроме того, как я понимаю, $? по-прежнему будет содержать код возврата второй команды в канале, потому что назначения переменных, подстановки команд и составные команды эффективно прозрачны для возврата код команды внутри них, поэтому возвращаемый статус команды2 должен быть распространен - ​​это и не нужно определять дополнительную функцию, поэтому я считаю, что это может быть несколько лучше, чем предложение, предложенное lesmana.

В соответствии с предостережениями lesmana, возможно, что команда 1 в какой-то момент закончит использование файловых дескрипторов 3 или 4, поэтому, чтобы быть более надежными, вы сделали бы:

exec 4 & 1
exitstatus = `{{command1 3> & -; printf $? 1 > & амп; 3; } 4 & - | command2 1 & 4; } 3 & 1 & quot;
exec 4 &

Обратите внимание, что я использую составные команды в моем примере, но подоболочки (используя () вместо {} также будут работать, но могут быть менее эффективными.)

Команды наследуют дескрипторы файлов из процесса, который запускает их, поэтому вся вторая строка наследует файловый дескриптор четыре, а составная команда, за которой следуют 3> & 1, наследует дескриптор файла три. Таким образом, 4 & - гарантирует, что команда внутреннего соединения не будет наследовать файловый дескриптор четыре, а 3> & - не будет наследовать дескриптор файла три, поэтому command1 получает «более чистую», более стандартную среду. Вы также можете перемещать внутренний 4> & - рядом с 3> & -, но я полагаю, что не просто ограничивать его область как можно больше.

Я не уверен, как часто вещи используют дескриптор файлов три и четыре непосредственно. Я думаю, что большинство программ времени используют системные вызовы, которые возвращают дескрипторы файлов, не используемых в момент времени, но иногда код записывает в файл дескриптор 3, я думаю (я мог бы представить себе программу, проверяющую файловый дескриптор, чтобы увидеть, открыт ли он, и использовать его, если это так, или вести себя по-другому, если это не так). Поэтому последнее, вероятно, лучше всего учитывать и использовать для случаев общего назначения.

ответил mtraceur 5 J0000006Europe/Moscow 2015, 07:43:45
9

Если у вас установлен пакет moreutils , вы можете использовать утилиту mispipe , которая делает именно то, что вы спросили.

ответил Emanuele Aina 8 J0000006Europe/Moscow 2013, 15:22:57
6

Решение lesmana выше также может быть выполнено без накладных расходов на запуск вложенных подпроцессов с помощью {..} (помня, что эта форма сгруппированных команд всегда должна заканчиваться точкой с запятой). Что-то вроде этого:

{{{{someprog; echo $? > & амп; 3; } | фильтр> & 4; } 3 & 1; } | stdintoexitstatus; } 4 & 1

Я проверил эту конструкцию с тире версии 0.5.5 и bash версии 3.2.25 и 4.2.42, поэтому, даже если некоторые оболочки не поддерживают группировку {..}, это все еще совместимый с POSIX.

ответил pkeller 22 52013vEurope/Moscow11bEurope/MoscowFri, 22 Nov 2013 16:50:21 +0400 2013, 16:50:21
4

Это переносимое, то есть работает с любым совместимым с POSIX оболочкой, не требует, чтобы текущий каталог был доступен для записи, и позволяет нескольким сценариям использовать один и тот же трюк для запуска одновременно.

(foo; echo $?> /tmp /_ $$) | (bar; exit $ (cat /tmp /_ $$; rm /tmp /_ $$))

Изменить: вот более сильная версия, следующая за комментариями Жиля:

(s = /tmp /.$$_$ RANDOM; ((foo; echo $?> $ s) | (bar)); exit $ (cat $ s; rm $ s))

Edit2: и вот немного более легкий вариант, следующий за сомнительным комментарием:

(s = /tmp /.$$_$ RANDOM; {foo; echo $?> $ s;} | bar; exit $ (cat $ s; rm $ s))
ответил jlliagre 3 J0000006Europe/Moscow 2011, 03:17:27
4

В качестве дополнения к ответу @Patrik следует понимать следующее: если вы не можете использовать одно из общих решений.

Этот ответ предполагает следующее:

  • У вас есть оболочка, которая не знает $ PIPESTATUS и set -o pipefail
  • Вы хотите использовать канал для параллельного выполнения, поэтому нет временных файлов.
  • Вы не хотите иметь дополнительный беспорядок, если прервите скрипт, возможно, внезапным отключением питания.
  • Это решение должно быть относительно легко следовать и чистить для чтения.
  • Вы не хотите вводить дополнительные подоболочки.
  • Вы не можете играть с существующими файловыми дескрипторами, поэтому stdin /out /err нельзя трогать (однако вы можете временно ввести некоторые новые)
  

Дополнительные предположения. Вы можете избавиться от всех, но это слишком сильно забивает рецепт, поэтому он не рассматривается здесь:

     
  • Все, что вы хотите знать, это то, что все команды в PIPE имеют код выхода 0.
  •   
  • Вам не нужна дополнительная информация о боковой полосе.
  •   
  • Ваша оболочка ждет, пока все команды команд вернутся.
  •   

До: foo | бар | baz, однако это возвращает код выхода последней команды (baz)

Требуется: $? не должен быть 0 (true), если какая-либо из команд в трубе терпит неудачу

После:

TMPRESULTS = "` mktemp`"
{
rm -f "$ TMPRESULTS"

{foo || echo $? > & амп; 9; } |
{bar || echo $? > & амп; 9; } |
{baz || echo $? > & амп; 9; }
#Подождите
! читать TMPRESULTS & 8
} 9> "$ TMPRESULTS" 8 <"$ TMPRESULTS"

# $? теперь 0, только если все команды имеют код выхода 0

Разъяснения:

  • Временной файл создается с помощью mktemp. Обычно это создает файл в /tmp
  • Этот временный файл затем перенаправляется на FD 9 для записи и FD 8 для чтения
  • Затем временный файл немедленно удаляется. Тем не менее, он остается открытым, пока оба FD не исчезнут.
  • Теперь труба запущена. Каждый шаг добавляет только FD 9, если произошла ошибка.
  • wait необходим для ksh, потому что ksh else не ждет завершения всех команд команд. Однако обратите внимание, что есть нежелательные побочные эффекты, если присутствуют некоторые фоновые задачи, поэтому я прокомментировал это по умолчанию. Если ожидание не повредит, вы можете прокомментировать его.
  • Затем содержимое файла считывается. Если он пуст (потому что все работало) read возвращает false, поэтому true указывает на ошибку

Это может быть использовано в качестве замены плагина для одной команды и требуется только следующее:

  • Неиспользуемые FD 9 и 8
  • Одна переменная среды для хранения имени временного файла
  • И этот рецепт можно адаптировать к любой оболочке, которая позволяет перенаправление IO
  • Также он довольно несовместим с платформой и не нуждается в таких вещах, как /proc /fd /N

ошибка:

В этом скрипте есть ошибка, если пробел /tmp заканчивается. Если вам нужна защита от этого искусственного случая, вы можете сделать это следующим образом, однако это имеет тот недостаток, что число 0 в 000 зависит от количества команды в трубе, поэтому он немного сложнее:

TMPRESULTS = "` mktemp`"
{
rm -f "$ TMPRESULTS"

{foo; printf "% 1s" "$?" > & амп; 9; } |
{ бар; printf "% 1s" "$?" > & амп; 9; } |
{baz; printf "% 1s" "$?" > & амп; 9; }
#Подождите
читать TMPRESULTS & 8
[000 = "$ TMPRESULTS"]
} 9> "$ TMPRESULTS" 8 <"$ TMPRESULTS"

Заметки о переносимости:

  • ksh и аналогичные оболочки, которые ждут только команды последнего канала, нуждаются в wait uncommented

  • В последнем примере вместо echo -n "$?" используется printf "% 1s" "$?", потому что это более переносимо. Не каждая платформа правильно интерпретирует -n.

  • printf "$?" будет делать это также, однако printf "% 1s" ловит некоторые угловые случаи, если вы запускаете скрипт на некоторых действительно сломанная платформа. (Читайте: если вы случайно запрограммируете paranoia_mode = extreme.)

  • FD 8 и FD 9 могут быть выше на платформах, поддерживающих несколько цифр. AFAIR совместимая оболочка POSIX должна поддерживать только отдельные цифры.

  • Был протестирован с Debian 8.2 sh, bash, ksh, ash, sash и даже csh

ответил Tino 17 22015vEurope/Moscow11bEurope/MoscowTue, 17 Nov 2015 15:26:51 +0300 2015, 15:26:51
3

С некоторой осторожностью это должно работать:

foo-status = $ (mktemp -t)
(foo; echo $?> $ foo-status) | бар
foo_status = $ (cat $ foo-status)
ответил alex 3 J0000006Europe/Moscow 2011, 00:40:55
2

Следующий блок «if» будет работать только в том случае, если «команда» выполнена успешно:

if command; тогда
   # ...
фи

В частности, вы можете запускать что-то вроде этого:

haconf_out = /путь /к /некоторые /временный /файл

если haconf -makerw> "$ haconf_out" 2 & 1; тогда
   grep -iq "Кластер уже доступен для записи" "$ haconf_out"
   # ...
фи

Запустится haconf -makerw и сохранит его stdout и stderr в "$ haconf_out". Если возвращаемое значение из haconf истинно, тогда будет выполнен блок «if», а grep будет читать «$ haconf_out», пытаясь сопоставить его с «Cluster, уже доступным для записи» ».

Обратите внимание, что трубы автоматически очищаются; с переадресацией вам нужно будет осторожно удалить «$ haconf_out», когда это будет сделано.

Не так элегантно, как pipefail, но является законной альтернативой, если эта функциональность недоступна.

ответил Rany Albeg Wein 22 AMpMon, 22 Apr 2013 04:37:25 +040037Monday 2013, 04:37:25
0
Альтернативный пример для решения @lesmana, возможно, упрощенного.
Обеспечивает ведение журнала в файл, если это необходимо.
=====
$ cat z.sh
TEE = "кошка"
# TEE = "tee z.log"
# TEE = "tee -a z.log"

exec 8>> & &
{
  {
    {
      {#BEGIN - добавить код ниже этой строки и до #END
./zz.sh
echo $ {?} 1 & 8 # использует ровно 1x до #END
      #КОНЕЦ
      } 2 & 1 | $ {TEE} 1 & 9
    } 8 & 1
  } | exit $ (read; printf "$ {REPLY}")
} 9 & 1

exit $ {?}
$ cat zz.sh
echo "мой код сценария ..."
выход 42
$ ./z.sh; echo "status = $ {?}"
мой код сценария ...
статус = 42
$
ответил C.G. 27 52015vEurope/Moscow11bEurope/MoscowFri, 27 Nov 2015 03:18:12 +0300 2015, 03:18:12
-1

EDIT . Этот ответ неверен, но интересен, поэтому я оставлю его для справки в будущем.


Добавление команды ! в команду инвертирует код возврата.

http://tldp.org/LDP/abs/html/exit- status.html

# =========================================================================================== ================ # #
# Предшествующий _pipe_ с! инвертирует возвращаемый статус выхода.
ls | bogus_command # bash: bogus_command: команда не найдена
echo $? # 127

! ls | bogus_command # bash: bogus_command: команда не найдена
echo $? # 0
# Обратите внимание, что! не меняет исполнение трубы.
# Изменяется только статус выхода.
# ================================================================================================== ========== # #
ответил Falmarri 3 J0000006Europe/Moscow 2011, 02:09:45

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132