Передать stdio в функцию

Нубский вопрос!

У меня есть функция в командной строке, которая принимает параметры

sqr() { echo $(( $1 * $1 )) ; }

sqr 4 эхо 16. отлично.

Теперь я хочу передать вывод в эту функцию

echo 4 | sqr

Я получаю сообщение «синтаксическая ошибка: ожидается операнд (токен ошибки — «*»)» — очевидно, что $1 пуст.

Я пробовал использовать xargs с различными параметрами для передачи stdio в функцию, но получаю ошибки «Нет такого файла или каталога». Я упускаю что-то очевидное?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
72
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Проблема с xargs заключается в том, что он использует подоболочку, поэтому ваша функция не наследуется в новой оболочке.

Если вы создадите новую команду, доступную в PATH, вы можете использовать xargs и добавить функцию, позволяющую динамически использовать STDIN или аргументы::

~/bin/sqr:

#!/bin/bash

if [[ $1 ]]; then
    arg=$1
else
    arg=$(cat)
fi
echo $(( $arg * $arg ))
chmod +x ~/bin/sqr
PATH=~/bin:$PATH

Сейчас:

$ echo 6 | xargs -n1 sqr
36

$ echo 6 | sqr 
36

$ sqr 6
36

Часть arg=$(cat) хлебает STDIN в переменной arg.

Вы могли бы упомянуть, что экспорт функции позволяет использовать ее с xargs, как упоминалось здесь

Aaron 01.03.2024 14:58

@Аарон: попробуй :)

Gilles Quénot 01.03.2024 15:03

Неважно, вам нужно использовать xargs bash -c, что не так элегантно, как использование скрипта.

Aaron 01.03.2024 16:21
Ответ принят как подходящий

Одним из способов может быть использование расширения параметра на $1, чтобы cat сбросить STDIN в качестве резерва:

$ sqr() { n=${1:-$(cat)}; echo $((n*n)); }

$ sqr 6
36

$ echo 5 | sqr
25

$ echo 5 | sqr 6  # $1 takes precedence over STDIN
36

Примечание: это всего лишь улучшение по сравнению с моим предыдущим ответом, а не решение ошибки file not found ^^

Gilles Quénot 01.03.2024 15:22

@Gilles Насколько я понимаю, фактической ошибкой была «синтаксическая ошибка: ожидается операнд» (поскольку $1 не предоставляет содержимое из STDIN, что можно устранить, как показано с использованием расширения параметров). Ошибка «Нет такого файла или каталога» была попыткой OP решить проблему с помощью xargs, превратив ее в проблему XY.

pmf 01.03.2024 15:33

Ой, да, извините за последнее «утверждение»

Gilles Quénot 01.03.2024 15:42

Мне нравится элегантность этого ответа, он многому научил меня расширению параметров. Я собираюсь попытаться использовать свои новые способности для написания функции, которая может делать то же самое с двумя параметрами. $(cat), похоже, это не нравится, так что пришло время поэкспериментировать. Большое спасибо за новую кроличью нору. ржу не могу

Mark Giddens 04.03.2024 22:23

Параметры и стандартный ввод — это два совершенно разных способа взаимодействия вызывающего объекта с функцией:

sqr_param () {
    local n
    n=$1
    echo $(( $n * $n ))
}

sqr_stdin () {
    local n
    read -r n
    echo $(( $n * $n ))
}

Записав их обе в довольно подробной форме, вы увидите, что одна («нечистая») версия может быть тривиально реализована в терминах другой («чистой») версии.

# This one focuses on math
sqr () {
    echo $(( $1 * $1 ))
}

# This one provides an interface with the outside world
sqr_stdin () {
    local n
    read -r n
    sqr "$n"
}

Я бы сопротивлялся искушению реализовать одну функцию, которая могла бы обрабатывать либо аргумент, либо стандартный ввод. «Столько сложностей в программном обеспечении возникает из-за попыток заставить одну вещь выполнять две вещи».

Не согласен. Хорошее программное обеспечение обрабатывает как STDIN, так и аргументы.

Gilles Quénot 01.03.2024 15:44

«Хорошее программное обеспечение» и «одна функция» — это не одно и то же.

chepner 01.03.2024 15:44
$ sqr() {
    local n tmpfd
    exec {tmpfd}<&0             # Save value of FD 0 (stdin) in "tmpfd"
    (( $# )) && exec <<<"$1"    # If an argument passed in create a
                                # temp file, store that value in it,
                                # then set FD 0 to point to that file.
    IFS= read -r n              # Read a value from FD 0
    exec 0<&"$tmpfd"            # Restore FD 0 to the value in "tmpfd"
    echo $(( $n * $n ))         # Print the result of the math
}

$ sqr 4
16

$ echo 4 | sqr
16

В приведенном выше примере используется именованный файловый дескриптор tmpfd, доступный в bash 4.1 или более поздней версии. См. https://mywiki.wooledge.org/FileDescriptor для получения дополнительной информации о файловых дескрипторах.

Альтернативно, если вы определяете тело функции в подоболочке, вам не нужно сохранять и восстанавливать FD 0, поскольку директива exec будет применяться только внутри подоболочки:

$ sqr() {
  (                             # Start a subshell
    local n
    (( $# )) && exec <<<"$1"    # If an argument passed in create a
                                # temp file, store that value in it,
                                # then set FD 0 to point to that file.
    IFS= read -r n              # Read a value from FD 0
    echo $(( $n * $n ))         # Print the result of the math
  )                             # End the subshell
}

$ sqr 4
16

$ echo 4 | sqr
16

Выглядит ненужно запутанно

Gilles Quénot 02.03.2024 00:57

А если вы хотите использовать подоболочку, подумайте о sqr() ( var=$1; echo $(( $var * $var )) )

Gilles Quénot 02.03.2024 01:03

Я знаю, что мог бы просто использовать (...), но я не сторонник менять синтаксис {...}.

Ed Morton 02.03.2024 04:51

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