如何 return 到 bash 脚本中的特定命令?

How to return to specific command in bash script?

我需要一种方法来跟踪 bash 脚本中最后一次成功执行的命令。例如我有这个脚本:

#!/bin/bash
command1
command2

command1command2 都会 return 一些退出代码。我需要的是保存最后一次成功执行的命令,当我重新运行这个脚本时,我希望它从那一点开始。因此,如果 command1 正确执行而 command2 失败,下次我运行脚本时它将从 command2.

开始

最简单的方法是将执行信息存储在附加文件中,脚本将在执行前读取这些信息。在执行命令之前,脚本会检查该命令是否已经成功执行。

有更好的方法吗?有没有这个实现的例子?我想我在配置脚本中看到了类似的东西。

你想在以后的任意时间重新启动脚本吗(那会很难),还是你只需要暂时 运行 一个 shell 来执行一些命令然后回到剧本?如果是后者,你可以这样做:

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

如果 cmd2 失败,您应该得到 shell 提示。做你想做的。完成后,如果您希望脚本在 cmd3 处启动,请使用 exit 0 退出 shell。如果要中止,请使用 exit 1.

退出 shell
#!/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 将 return undef(perl 的 false),否则 returns 输出。其次,您将需要修复它,一旦列出的全部 'completed' perl 再次将它们更改为所有未决。

#!/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
   "$@"
   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

这可能不是您正在寻找的答案,但一个简单的 Makefile 似乎可以满足您的要求。 Make 开箱即用地终止执行,其目的是避免重复处理步骤。特别是如果(至少大部分)你的命令生成一个输出文件,那将是一个相当自然的选择。如果不是,保留一个简单的状态文件是一个常见的 Make 习惯用法,用于将命令序列标记为成功完成。

(如果您是 Make 的新手,请注意目标中的每个命令都需要有一个用于缩进的文字标签。)

.PHONY: all
all: .command1-done .command2-done
.command1-done:
    command1
    touch $@
# Comment out the dependency to avoid forcing sequential execution
.command2-done: .command1-done
    command2
    touch $@

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

因此,这个 运行s command1 如果成功,则继续执行配方中的下一个命令,即信号量文件上的 运行s touch (Make 变量 $@ 扩展为当前目标名称)。如果这也成功,它 运行s command2,如果成功则类似地创建它的 "done" 文件。 运行 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 >$@
wc.out: grep.out
    wc -l <$< >$@

(不幸的是,即使目标失败,shell 重定向也会创建输出文件。解决方法是使用临时输出文件,然后仅在成功时将其移动到位:

.PHONY: all
all: grep.out wc.out
grep.out:
    grep -Fw Debian /etc/motd >.$@.tmp
    mv .$@.tmp $@
wc.out: grep.out
    wc -l <$< >.$@.tmp
    mv .$@.tmp $@

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

但无论如何,这已经太长了。)