Як скласти функції баш за допомогою труб?


18

У мене кілька функцій, визначених таким чином:

function f {
  read and process $1
  ...
  echo $result
}

Я хочу скласти їх разом, щоб виглядала виклик f | g | h.

Яку ідіому потрібно використовувати для перетворення функції, що працює над аргументами, в аргументи для читання з stdin? Чи можна читати пари, кортежі аргументів із потоку, не виходячи з них (наприклад, нульове закінчення)?


Або потрібно щось подібне, h(g(f(...)))або кожна з функцій читається зі стандартного вводу ( read x; ...) і записується на стандартний вихід ( echo ...).
vonbrand

Відповіді:


21

Одним із потенційних підходів було б скласти while...readконструкцію всередині ваших функцій, яка б обробляла будь-які дані, що надходили у функцію через STDIN, працювала над нею, а потім випромінювала отримані дані назад через STDOUT.

function X {
  while read data; do
    ...process...
  done
}

Потрібно бути обережним з налаштуванням while ..read..компонентів, оскільки вони будуть сильно залежати від типів даних, які вони зможуть надійно споживати. Можливо, існує оптимальна конфігурація, яку ви можете придумати.

Приклад

$ logF() { while read data; do echo "[F:$(date +"%D %T")] $data"; done; }
$ logG() { while read data; do echo "G:$data";                    done; }
$ logH() { while read data; do echo "H:$data";                    done; }

Ось кожна функція сама по собі.

$ echo "hi" | logF
[F:02/07/14 20:01:11] hi

$ echo "hi" | logG
G:hi

$ echo "hi" | logH
H:hi

Ось вони, коли ми використовуємо їх разом.

$ echo "hi" | logF | logG | logH
H:G:[F:02/07/14 19:58:18] hi

$ echo -e "hi\nbye" | logF | logG | logH
H:G:[F:02/07/14 19:58:22] hi
H:G:[F:02/07/14 19:58:22] bye

Вони можуть приймати різні стилі введення.

#-- ex. #1
$ cat <<<"some string of nonsense" | logF | logG | logH
H:G:[F:02/07/14 20:03:47] some string of nonsense

#-- ex. #2    
$ (logF | logG | logH) <<<"Here comes another string."
H:G:[F:02/07/14 20:04:46] Here comes another string.

#-- ex. #3
$ (logF | logG | logH)
Look I can even
H:G:[F:02/07/14 20:05:19] Look I can even
type to it
H:G:[F:02/07/14 20:05:23] type to it
live
H:G:[F:02/07/14 20:05:25] live
via STDIN
H:G:[F:02/07/14 20:05:29] via STDIN
..type Ctrl + D to stop..

#-- ex. #4
$ seq 5 | logF | logG | logH
H:G:[F:02/07/14 20:07:40] 1
H:G:[F:02/07/14 20:07:40] 2
H:G:[F:02/07/14 20:07:40] 3
H:G:[F:02/07/14 20:07:40] 4
H:G:[F:02/07/14 20:07:40] 5

#-- ex. #5
$ (logF | logG | logH) < <(seq 2)
H:G:[F:02/07/14 20:15:17] 1
H:G:[F:02/07/14 20:15:17] 2

4

Як доповнення до відповіді slm , я зробив кілька експериментів із розділеними нулями кортежами як аргументи функції:

$ sayTuple() { 
    IFS= read -r -d $'\0' d1
    IFS= read -r -d $'\0' d2
    echo "sayTuple: -$d1- -$d2-"
}

Примітки: sayTupleдвічі зчитує запис з нульовим завершенням, -d $'\0'обробляючи будь-який простір, що оточує вхід IFS=. echoназад записи в оточенні-

Результат показує, що він правильно обробляє нульовий сигнал, що містить \nі \t:

$ printf "%s\0%s\0" "Hello " $' Brave\n\tWorld' | sayTuple 
sayTuple: -Hello - - Brave
        World-

Будь ласка, додайте пропозиції щодо покращення в коментарях, це цікава тема.


+1 як ваша ідея. Ми можемо поставити цикл всередину, який би дозволив йому приймати довільні # аргументи. sayTuple() { arr=() ; while IFS= read -r -d $'\0' arg; do arr+="$arg"; done; echo "sayTuple: ${arr[@]}"; }.
slm

Здається, ви повинні вміти робити, IFS= read -r -d $'\0' -a argале я не міг змусити це працювати. Це дозволило б видалити те while, що видається непотрібним, але це був єдиний зразок, який я міг приступити до роботи.
slm
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.