2012-03-31 2 views
31

Почему эта работавыполнить функцию с таймаутом

timeout 10s echo "foo bar" # foo bar 

, но это не

function echoFooBar { 
    echo "foo bar" 
} 

echoFooBar # foo bar 

timeout 10s echoFooBar # timeout: failed to run command `echoFooBar': No such file or directory 

и будет, как я могу заставить его работать?

+0

http://stackoverflow.com/questions/12321469/retry-a-bash-command-with-timeout/35977896#35977896 –

ответ

32

timeout - это команда - поэтому она выполняется в подпроцессе вашей оболочки bash. Поэтому он не имеет доступа к вашим функциям, определенным в вашей текущей оболочке.

Дана команда timeout Выполняется как подпроцесс тайм-аута - процесс с большими дочерними элементами вашей оболочки.

Возможно, вы сбиты с толку, потому что echo - это как встроенная оболочка, так и отдельная команда.

Что вы можете сделать, это поместить вашу функцию в свой собственный файл сценария, chmod это исполняемый файл, а затем выполнить его с помощью timeout.

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

+0

спасибо за ваше решение! Но поскольку я хочу добавить тайм-аут в качестве дополнительной опции для существующего скрипта, было бы совершенно неудобно иметь собственный файл только для функций таймаута. Это единственное решение? – speendo

+4

@speendo Считайте, что 'timeout' работает, убивая процессы, отправляя им сигналы - это то, что вы можете делать только с процессами. Поэтому все, что вы делаете с тайм-аутом, должно быть собственным процессом. –

+1

@speendo Также обратите внимание, что bash является (AFAIK) однопоточным, и что может сделать функция таймаута, если поток выполняет вашу функцию? –

3

Если вы просто хотите добавить тайм-аут в качестве дополнительной опции для всего существующего скрипта, вы можете проверить его на параметр тайм-аута, а затем заставить его вызывать его рекурсивно без этой опции.

example.sh:

#!/bin/bash 
if [ "$1" == "-t" ]; then 
    timeout 1m $0 $2 
else 
    #the original script 
    echo $1 
    sleep 2m 
    echo YAWN... 
fi 

работает этот скрипт без таймаута:

$./example.sh -other_option # -other_option 
          # YAWN... 

запуская его с одной минуты ожидания:

$./example.sh -t -other_option # -other_option 
16

Там в инлайн альтернатива также запускает подпроцесс оболочки bash:

 

timeout 10s bash <<EOT 
function echoFooBar { 
    echo foo 
} 

echoFooBar 
sleep 20 
EOT 
 
8

Вы можете создать функцию, которая позволит вам делать то же самое, как тайм-аут, но и для других функций:

function run_cmd { 
    cmd="$1"; timeout="$2"; 
    grep -qP '^\d+$' <<< $timeout || timeout=10 

    ( 
     eval "$cmd" & 
     child=$! 
     trap -- "" SIGTERM 
     (  
       sleep $timeout 
       kill $child 2> /dev/null 
     ) &  
     wait $child 
    ) 
} 

И может работать, как показано ниже:

run_cmd "echoFooBar" 10 

Примечание: Решение пришел один из моих вопросов: Elegant solution to implement timeout for bash commands and functions

+0

не следует ли убивать самую внутреннюю подоболочку после 'wait $ child'? он не делает ничего вредного (кроме ожидания), но он все равно продолжает подсчитывать, даже если ребенок закончил – Blauhirn

2
function foo(){ 
    for i in {1..100}; 
    do 
     echo $i; 
     sleep 1; 
    done; 
} 

cat <(foo) # Will work 
timeout 3 cat <(foo) # Will Work 
timeout 3 cat <(foo) | sort # Wont work, As sort will fail 
cat <(timeout 3 cat <(foo)) | sort -r # Will Work 
19

Как Дуглас Лидер сказал, что вам нужен отдельный процесс таймаута для сигнала. Обходной путь путем экспорта функции в подоболочки и запуска подоболочки вручную.

export -f echoFooBar 
timeout 10s bash -c echoFooBar 
+0

Экспорт -f не работает в sh, делает в bash. – J0hnG4lt

Смежные вопросы