Читать стандартный ввод в скрипте bash

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

  • найти-MODELNAME-в-EPSON-файлов PPD
  • найти-MODELNAME-в-Samsung-файлов PPD
  • найти-MODELNAME-в-HP-файлов PPD
  • так далее...

Я писал функции, которые читают вывод и фильтруют его:

function filter-epson {
    find-modelname-in-epson-ppds | sed <bla-blah-blah>
}

function filter-hp {
    find-modelname-in-hp-ppds | sed <the same bla-blah-blah>
}
etc ...

Но я подумал, что было бы лучше сделать что-то вроде этого:

function filter-general {
    (somehow get input) | sed <bla-blah-blah>
}

а затем вызвать другие высокоуровневые функции:

function high-level-func {
    # outputs filtered information
    find-modelname-in-hp/epson/...-ppds | filter-general 
}

Как я могу добиться этого с помощью лучших практик Bash?

3 ответа

Решение

Если вопрос How do I pass stdin to a bash function?тогда ответ таков:

Функции шеллскрипта принимают stdin обычным способом, как если бы они были командами или программами.:)

input.txt:

HELLO WORLD
HELLO BOB
NO MATCH

test.sh:

#!/bin/sh

myfunction() {
    grep HELLO
}

cat input.txt | myfunction

Выход:

hobbes@metalbaby:~/scratch$ ./test.sh 
 HELLO WORLD 
 HELLO BOB 

Обратите внимание, что аргументы командной строки также обрабатываются обычным способом, например так:

test2.sh:

#!/bin/sh

myfunction() {
    grep "$1"
}

cat input.txt | myfunction BOB

Выход:

hobbes@metalbaby:~/scratch/$ ./test2.sh 
 HELLO BOB 

Чтобы быть больно явным, что я пипец со стандартного ввода, я иногда пишу

cat - | ...

А очень простые средства, чтобы получить стандартный ввод в переменную является использованиеread. По умолчанию он читает дескриптор файла "0", т.е. stdin, т.е./dev/stdin.

Пример функции:

input(){ local in; read in; echo you said $in; }                    

Пример реализации:

echo "Hello World" | input               

Результат:

ты сказал привет мир

Дополнительная информация

Конечно, вам не нужно объявлять переменную как локальную. Я просто включил это для хорошей формы. Обычный старыйread in делает то, что вам нужно.

Итак, вы понимаете, как readработает, по умолчанию он считывает данные из указанного файлового дескриптора (или неявного stdin) и блокируется, пока не встретит новую строку. В большинстве случаев вы обнаружите, что это будет неявно прикреплено к вашему вводу, даже если вы об этом не знали. Если у вас есть функция, которая, кажется, "зависает", с этим механизмом просто помните об этом (есть и другие способы использованияread чтобы разобраться с этим).

Более надежные решения

В дополнение к предыдущему примеру, вот вариант, который позволяет передавать ввод через стандартный ввод ИЛИ аргумент:

input()
{ 
    local in=$1 if [ -z "$in" ]; then read in; fi
    echo you said $in
}

С помощью этой настройки вы ТАКЖЕ можете вызвать функцию, например:

input "Hello World"

Как насчет обработки параметра stdin и других нескольких аргументов? Многие стандартные утилиты nix, особенно те, которые обычно работают с stdin/stdout, придерживаются общей практики обработки тире.- для обозначения "по умолчанию", что в контексте означает либо стандартный ввод, либо стандартный вывод, поэтому вы можете следовать соглашению и рассматривать аргумент, указанный как - означать "стандартный ввод":

input()
{ 
    local a=$1; if [ "$a" == "-" ]; then read a; fi
    local b=$2
    echo you said $a $b
}

Назовите это так:

input "Hello" "World"

или

echo "Hello" | input - "World"

Если пойти еще дальше, на самом деле нет причин ограничивать stdin только тем, что он является опцией только для первого аргумента! Вы могли бы создать сверхгибкую функцию, которая могла бы использовать ее для любого из них...

input()
{ 
    local a=$1; if [ "$a" == "-" ]; then read a; fi
    local b=$2; if [ "$b" == "-" ]; then read b; fi
    echo you said $a $b
}

Зачем тебе что? Потому что вы могли бы сформулировать и трубы в любой аргумент может понадобиться...

myFunc | input "Hello" -

В этом случае я передаю второй аргумент, используя результаты myFunc а не единственный, у кого есть вариант для первого.

Вызов sed непосредственно. Вот и все.

function filter-general {
    sed <bla-blah-blah>
}
Другие вопросы по тегам