LINUX.ORG.RU

bash, как вернуть результат из функции, выполняемой в фоне

 


0

2

А как в bash при запуске функции в фоне вернуть результат? Про fifo знаю.

#!/usr/bin/env bash

declare -A results=([test1]=TEST1)
results["test2"]="TEST2"

# Function to perform a task
function search_param {
    local -n res=$3

    echo "Searching $1 on $2"
    sleep 2
    echo "Searchin on $2 completed"
    res[$2]="searching $2"
}

# Array to hold background process IDs
PIDS=()

param='1234'
declare -a hosts=("host1" "host2" "host3")

# Execute tasks in separate threads
for host in "${hosts[@]}"; do
    search_param $param $host results &
    PIDS+=($!)
done

# Wait for all threads to finish
for pid in "${PIDS[@]}"; do
    wait $pid
done

echo "All threads completed."

for i in "${!results[@]}"; do echo "${i}: ${results[$i]}"; done

exit 0

если запускать не в фоне

search_param $param $host results

то скрипт правильно отработает, но в 3 раза дольше, чем хотелось бы.

В принципе понимаю, почему так происходит. Хотелось бы найти решение.


Можно запустить сабшел с перенеаправлением его stdout в агрегатор результатов. А из этого сабшела нафоркать воркеров, они унаследуют stdout сабшела. Воркеры должны выводить результат в stdout. Как-то так:

worker() {
        sleep 3
        stat -L /proc/self/fd/1
}

(for n in 1 2 3; do worker & done) | cat
iliyap ★★★★★
()
Ответ на: комментарий от iliyap

в принципе, это тоже самое что открыть временное fifo, и писать в него результаты, а после завершения прочитать и удалить.

Только fifo обеспечивает атомарность записи, а stdout вроде бы нет - буфер сбрасывается, например, символом \n.

Как прокинуть pipe из родительского в дочерний процесс не разобрался.

zudwa
() автор топика

Здравствуйте, найдётся ли у вас минутка поговорить о нашем Господине и Спасителе (от ужасов posix shell) Бабашке?

В нормальном яп, подобная задача решается так. Пусть у нас есть медленная команда /tmp/slow.sh:

#!/bin/sh

id="$1"
delay=$((1 + $RANDOM % 10))

sleep $delay
echo "Задача №$id завершилась с каким-то результатом за ${delay}с"

Тогда её можно запускать в фоне с помощью babashka.process

#!/usr/bin/env bb

(ns lor
  (:require [babashka.process :refer [process]]))

(defn run-slow-cmd [id]
  (process {:out :string} "/tmp/slow.sh" id))

(defn worker [ids]
  (->> ids
       (map run-slow-cmd)
       (map deref)
       (map :out)
       println))

(defn -main [args]
  (-> args
      first
      Integer/parseInt
      range
      worker)
  )

(-main *command-line-args*)

После чего можно пользоваться как

time ./lor.bb 10
(Задача №0 завершилась с каким-то результатом за 2с
 Задача №1 завершилась с каким-то результатом за 2с
 Задача №2 завершилась с каким-то результатом за 5с
 Задача №3 завершилась с каким-то результатом за 9с
 Задача №4 завершилась с каким-то результатом за 5с
 Задача №5 завершилась с каким-то результатом за 6с
 Задача №6 завершилась с каким-то результатом за 9с
 Задача №7 завершилась с каким-то результатом за 8с
 Задача №8 завершилась с каким-то результатом за 7с
 Задача №9 завершилась с каким-то результатом за 6с
)
9.054 secs

И никакой свистопляски с subshell’s, именнованными трубками и прочими унаследованными stdout. Бонусом предлагается нормальный ЯП из коробки.

ugoday ★★★★★
()