2015-01-01 2 views
1

Мне нужен способ отслеживания последней успешно выполненной команды в сценарии bash. Например, у меня есть этот скрипт:Как вернуться к определенной команде в сценарии bash?

#!/bin/bash 
command1 
command2 

command1 и command2 оба будут возвращать некоторый код выхода. Мне нужно сохранить последнюю успешно выполненную команду, и когда я заново запустил этот скрипт, я хочу, чтобы он начинался с этой точки. Так что если command1 выполнено правильно и command2 не удалось, в следующий раз, когда я запустил скрипт, он начнется с command2.

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

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

+3

Если вам действительно нужно отслеживать состояние * каждой команды * и повторное выполнение поддержки из любой произвольной точки, вам потребуется вручную записать каждый статус выхода файл и перед каждой строкой проверять, правильно ли работала команда на этой строке в последний раз (или создать временную копию сценария с удалёнными удаленными строками с помощью 'sed' или аналогичного). Но это звучит как неправильное решение для меня. В чем здесь проблема? (Что-то вроде 'make' действительно может быть полезным для этого потенциально также.) –

+0

Это должно быть bash? Для этого я бы использовал perl. Конечно, мои perl skils лучше, чем bash, и я уверен, что есть bash, которые могут быть более полезными, но если вам нравится, я могу показать вас в perl. – terary

+0

@ Этан Рейснер, любая реализация этого решения? У меня уже есть проверка кодов выхода команд. Я искал удобный способ вернуться к определенной функции. Мне нужно это для сценариев обновления/настройки, которые я хочу запустить на новых виртуальных машинах. – ton1c

ответ

3
#!/bin/bash -e 
# 
# run those commands that did not yet run 
# breaks upon failed command due to -e 
# 
# keeps the line number of the last successful command in lastline 
# 
# first, some preparation: 
# 

lastfile=$(readlink -f lastline) # inspired by ton1c: Get absolute path 
test -f $lastfile && lastline=`cat $lastfile` >/dev/null 
test -z "$lastline" && lastline=0 

e() 
{ 
    thisline="$BASH_LINENO" 
    test $lastline -lt $thisline || return 0 
    "[email protected]" 
    echo $thisline > $lastfile 
} 

# 
# and now prepend every command by e 
# for example run this, interrupt the sleep 5 and run again 
# to restart ALL commands, remove the file "lastline" 
# 

echo running sleep 3 
e sleep 3 
echo running sleep 5 
e sleep 5 
echo running sleep 7 
e sleep 7 
+0

Это очень умно. Это было бы улучшено, если бы был способ сбросить его. Тем не менее, я могу помочь подумать, что он начинает доходить до точки, где 'make' будет более надежным и гибким решением. – rici

+0

перезагрузите его, просто удалив файл «lastline». –

+0

Да, я получил это, но это утечка детали реализации. А что, если у вас есть несколько таких скриптов? Какое правильное имя файла? Вы могли бы, например, сделать что-то вроде: 'if [[$ 1 == --reset]]; затем сдвиг; rm -f lastline; fi', чтобы сбросить скрипт сам, если он вызван с '--reset' в качестве первого аргумента командной строки. (Более надежное решение не вписывается в комментарий :)) – rici

0

Вы хотите перезапустить скрипт в какое-то время в будущем (это будет сложно), или вам просто нужно временно запустить оболочку для выполнения некоторых команд, а затем вернуться к скрипту? Если последнее, вы можете сделать что-то вроде:

#!/bin/sh 
cmd1 || ${SHELL-bash} || exit 1 
cmd2 || ${SHELL-bash} || exit 1 
cmd3 || ${SHELL-bash} || exit 1 

Если cmd2 не удается, вы должны получить приглашение оболочки. Делай что хочешь. Когда вы закончите, если вы хотите, чтобы сценарий поднялся на cmd3, выйдите из оболочки с exit 0. Если вы хотите прервать, выйдите из оболочки с exit 1.

+0

Это действительно довольно умный сценарий стиля «точка останова». Единственная проблема, с которой он сталкивается, заключается в том, что он не позволяет вам легко повторно запустить заданную команду. Функция с циклом выбора и набором команд будет лучше (но также сложнее сделать с sh/bash). –

1
#!/usr/bin/perl 

use strict; 

open(COMMANDS,"commands.txt")||die "Couldn't open command file\n"; 
my @cmd = <COMMANDS>; #get all the file contents into array 
close(COMMANDS); 

#whacks file 
open(COMMANDS,">commands.txt")||die "Couldn't open command file\n"; 




foreach my $cmd (@cmd) 
    { chomp($cmd); 
    my($do,$status) = split(/:/,$cmd); 

    if($status =~ /pending/i) 
     { 
    my $return = qx($do); 

    if(!$return) 
     {$status='failed';} 
    else 
     { print "$do returned $return\n"; 
     $status ='completed'; 
     } 
     } 
    else 
     {$status ='pending';} 
    print COMMANDS "$do:$status\n"; 

} 
close(COMMANDS); 

содержание команды:

echo "fish":pending 
date +%D:pending 
whoamix:failed 
whoami:pending 

Вы должны выработать логику некоторых. qx вернет undef (perl's false), если команда не удалась, но в противном случае возвращает результат. Во-вторых, вам нужно будет исправить это, чтобы после того, как все перечисленные все были «завершены», perl меняет их на все вновь ожидающие.

+1

Это не разделяет состояние оболочки между командами и поэтому ограничивает то, что можно сделать несколько серьезно. Это может быть не проблема, но стоит отметить. Он также запрещает использование многолинейных команд и потенциально и, как написано, не может обрабатывать команды с ':' в них где угодно. –

+1

Приношу свои извинения.Мое намерение состояло в том, чтобы продемонстрировать, как делать то, что было указано в perl, а не bash. Я не уверен, что вы подразумеваете под «оболочечным состоянием»? Что касается многострочной линии и использования «:», то это легко устранить. Думаю, я не пытался решить вашу проблему, но чтобы помочь вам ее решить. – terary

+1

Каждая команда запускается в новой оболочке. Таким образом, вы не можете устанавливать переменные/и т. Д. и использовать их между командами, как вы можете, в обычном сценарии оболочки. И да, проблема ':' разбора легко фиксируется. –

0

Возможно, это не тот ответ, который вы ищете, но простой Makefile, похоже, удовлетворит ваши требования. Завершите выполнение ошибки из-за ошибки, и ее цель состоит в том, чтобы избежать повторения шагов обработки. Особенно, если (по крайней мере, большинство) ваши команды генерируют выходной файл, это было бы довольно естественным образом. Если нет, сохранение простого файла состояния - это обычная Make idiom, чтобы отметить последовательность команд как успешно завершенную.

(Если вы новичок сделать, заметьте, что каждая команда в мишени должна иметь вкладку буквальную для отступа.)

.PHONY: all 
all: .command1-done .command2-done 
.command1-done: 
    command1 
    touch [email protected] 
# Comment out the dependency to avoid forcing sequential execution 
.command2-done: .command1-done 
    command2 
    touch [email protected] 

.PHONY: clean 
clean: 
    rm -f .*-done 

Таким образом, это работает command1 и, в случае успеха, переходит к следующую команду в рецепте, которая запускает touch в файле семафора (переменная Make [email protected] расширяется до текущего целевого имени). Если это тоже удастся, он запускает command2 и аналогичным образом создает свой «готовый» файл, если он преуспевает. Бег make снова покажет вам, что ни одна из команд не нужно запускать:

$ make 
command1 
touch .command1-done 
command2 
touch .command2-done 

$ make 
make: Nothing to be done for `all'. 

Если вы на самом деле заботитесь о порядке выполнения целей, которые, как правило, потому, что существует зависимость (command2 выходит из строя или, что еще хуже, приведет к получению неверных результатов если он не запущен после command1).Такие зависимости нужно объявить - я приведу пример того, как объявлять такие отношения. Если нет зависимости, вы, вероятно, не должны объявлять ее; то Make будет запускать ваши цели в произвольном порядке (хотя версии, с которыми я работал, обычно предсказуемы); или вы можете запустить их параллельно с make -j.

(состояние файла обычно начинается с точки, чтобы держать его скрытые от обычного списка каталогов.)

Более реалистично, возможно, ваши команды на самом деле сгенерировать вывод:

.PHONY: all 
all: grep.out wc.out 
grep.out: 
    grep -Fw Debian /etc/motd >[email protected] 
wc.out: grep.out 
    wc -l <$< >[email protected] 

(К сожалению, . Shell перенаправление создаст выходной файл, даже если цель проваливает временное решение использовать временный выходной файл, а затем перенести его на место только на успех:

.PHONY: all 
all: grep.out wc.out 
grep.out: 
    grep -Fw Debian /etc/motd >[email protected] 
    mv [email protected] [email protected] 
wc.out: grep.out 
    wc -l <$< >[email protected] 
    mv [email protected] [email protected] 

.PHONY: clean 
clean: 
    rm -f .*.tmp 

Но так или иначе, это уже слишком длинное.)

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