如何 return 到 bash 脚本中的特定命令?
How to return to specific command in bash script?
我需要一种方法来跟踪 bash 脚本中最后一次成功执行的命令。例如我有这个脚本:
#!/bin/bash
command1
command2
command1
和 command2
都会 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
但无论如何,这已经太长了。)
我需要一种方法来跟踪 bash 脚本中最后一次成功执行的命令。例如我有这个脚本:
#!/bin/bash
command1
command2
command1
和 command2
都会 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
.
#!/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
但无论如何,这已经太长了。)