Нубский вопрос!
У меня есть функция в командной строке, которая принимает параметры
sqr() { echo $(( $1 * $1 )) ; }
sqr 4 эхо 16. отлично.
Теперь я хочу передать вывод в эту функцию
echo 4 | sqr
Я получаю сообщение «синтаксическая ошибка: ожидается операнд (токен ошибки — «*»)» — очевидно, что $1 пуст.
Я пробовал использовать xargs с различными параметрами для передачи stdio в функцию, но получаю ошибки «Нет такого файла или каталога». Я упускаю что-то очевидное?





Проблема с 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 bash -c, что не так элегантно, как использование скрипта.
Одним из способов может быть использование расширения параметра на $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 Насколько я понимаю, фактической ошибкой была «синтаксическая ошибка: ожидается операнд» (поскольку $1 не предоставляет содержимое из STDIN, что можно устранить, как показано с использованием расширения параметров). Ошибка «Нет такого файла или каталога» была попыткой OP решить проблему с помощью xargs, превратив ее в проблему XY.
Ой, да, извините за последнее «утверждение»
Мне нравится элегантность этого ответа, он многому научил меня расширению параметров. Я собираюсь попытаться использовать свои новые способности для написания функции, которая может делать то же самое с двумя параметрами. $(cat), похоже, это не нравится, так что пришло время поэкспериментировать. Большое спасибо за новую кроличью нору. ржу не могу
Параметры и стандартный ввод — это два совершенно разных способа взаимодействия вызывающего объекта с функцией:
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, так и аргументы.
«Хорошее программное обеспечение» и «одна функция» — это не одно и то же.
$ 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
Выглядит ненужно запутанно
А если вы хотите использовать подоболочку, подумайте о sqr() ( var=$1; echo $(( $var * $var )) )
Я знаю, что мог бы просто использовать (...), но я не сторонник менять синтаксис {...}.
Вы могли бы упомянуть, что экспорт функции позволяет использовать ее с xargs, как упоминалось здесь