变量作为 bash 脚本中的命令
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/794728/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Variables as commands in bash scripts
提问by wxs
I am writing a very simple bash script that tars a given directory, encrypts the output of that, and then splits the resultant file into multiple smaller files since the backup media don't support huge files.
我正在编写一个非常简单的 bash 脚本,它对给定目录进行 tar 压缩,加密该目录的输出,然后将生成的文件拆分为多个较小的文件,因为备份媒体不支持大文件。
I don't have a lot of experience with bash scripting. I believe I'm having issues with quoting my variables properly to allow spaces in the parameters. The script follows:
我在 bash 脚本方面没有很多经验。我相信我在正确引用我的变量以允许参数中有空格时遇到问题。脚本如下:
#! /bin/bash
# This script tars the given directory, encrypts it, and transfers
# it to the given directory (likely a USB key).
if [ $# -ne 2 ]
then
echo "Usage: `basename #! /bin/bash
if [ $# -ne 2 ]
then
echo "Usage: $(basename tar_cmd=( tar cv "$directory" )
split_cmd=( split -b 1024m - "$backup_file" )
encrypt_cmd=( openssl des3 -salt )
"${tar_cmd[@]}" | "${encrypt_cmd[@]}" | "${split_cmd[@]}"
) DIRECTORY BACKUP_DIRECTORY"
exit 1
fi
directory=
backup_directory=
current_date=$(date +%Y-%m-%dT%H-%M-%S)
backup_file="${backup_directory}/${current_date}.backup"
tar cv "$directory" | openssl des3 -salt | split -b 1024m - "$backup_file"
` DIRECTORY BACKUP_DIRECTORY"
exit 1
fi
DIRECTORY=
BACKUP_DIRECTORY=
BACKUP_FILE="$BACKUP_DIRECTORY/`date +%Y-%m-%dT%H-%M-%S.backup`"
TAR_CMD="tar cv $DIRECTORY"
SPLIT_CMD="split -b 1024m - \"$BACKUP_FILE\""
ENCRYPT_CMD='openssl des3 -salt'
echo "$TAR_CMD | $ENCRYPT_CMD | $SPLIT_CMD"
$TAR_CMD | $ENCRYPT_CMD | $SPLIT_CMD
say "Done backing up"
Running this command fails with:
运行此命令失败:
split: "foo/2009-04-27T14-32-04.backup"aa: No such file or directory
拆分:“foo/2009-04-27T14-32-04.backup”aa:没有这样的文件或目录
I can fix it by removing the quotes around $BACKUP_FILE
where I set $SPLIT_CMD
. But, if I have a space in the name of my backup directory it doesn't work. Also, if I copy and paste the output from the "echo" command directly into the terminal it works fine. Clearly there's something I don't understand about how Bash is escaping things.
我可以通过删除$BACKUP_FILE
我设置的位置周围的引号来修复它$SPLIT_CMD
。但是,如果我的备份目录名称中有空格,则它不起作用。另外,如果我将“echo”命令的输出直接复制并粘贴到终端中,它可以正常工作。显然,我不明白 Bash 是如何逃避事物的。
回答by Juliano
Simply don't put whole commands in variables. You'll get into a lot of trouble trying to recover quoted arguments.
只是不要将整个命令放在变量中。试图恢复引用的参数会遇到很多麻烦。
Also:
还:
- Avoid using all-capitals variable names in scripts. Easy way to shoot yourself on the foot.
- Don't use backquotes, use $(...) instead, it nests better.
- 避免在脚本中使用全大写的变量名。用脚射击自己的简单方法。
- 不要使用反引号,而是使用 $(...) ,它嵌套得更好。
tar_cmd() { tar cv "$directory"; }
split_cmd() { split -b 1024m - "$backup_file"; }
encrypt_cmd() { openssl des3 -salt; }
tar_cmd | split_cmd | encrypt_cmd
回答by Charles Duffy
eval
is not an acceptable practice if your directory names can be generated by untrusted sources. See BashFAQ #48for more on why eval
should not be used, and BashFAQ #50for more on the root cause of this problem and its proper solutions, some of which are touched on below:
eval
如果您的目录名称可以由不受信任的来源生成,则这是不可接受的做法。有关为什么不应该使用的更多信息,请参阅BashFAQ #48,有关此问题的根本原因及其正确解决方案的更多信息,请参见BashFAQ # 50,下面将介绍其中的一些:eval
If you need to build up your commands over time, use arrays:
如果您需要随着时间的推移构建命令,请使用数组:
eval $TAR_CMD | $ENCRYPT_CMD | $SPLIT_CMD
Alternately, if this is just about defining your commands in one central place, use functions:
或者,如果这只是在一个中心位置定义命令,请使用函数:
#! /bin/bash
if [ $# -ne 2 ]
then
echo "Usage: `basename export tar_create="tar cv"
export openssl="openssl des3 -salt"
export split_1024="split -b 1024m -"
` DIRECTORY BACKUP_DIRECTORY"
exit 1
fi
. standard_tools
directory=
backup_directory=
current_date=$(date +%Y-%m-%dT%H-%M-%S)
backup_file="${backup_directory}/${current_date}.backup"
${tar_create} "${directory}" | ${openssl} | ${split_1024} "$backup_file"
回答by Eddie
I am not sure, but it might be worth running an eval on the commands first.
我不确定,但首先对命令运行 eval 可能是值得的。
This will let bash expand the variables $TAR_CMD and such to their full breadth(just as the echo command does to the console, which you say works)
这将使 bash 将变量 $TAR_CMD 等扩展到它们的全部范围(就像 echo 命令对控制台所做的那样,您说它有效)
Bash will then read the line a second time with the variables expanded.
然后 Bash 将在变量扩展的情况下再次读取该行。
@tar_cmd = ( qw(tar cv), $directory );
@encrypt_cmd = ( qw(openssl des3 -salt) );
@split_cmd = ( qw(split -b 1024m -), $backup_file );
I just did a Google search and this page looks like it might do a decent job at explaining why that is needed. http://fvue.nl/wiki/Bash:_Why_use_eval_with_variable_expansion%3F
我刚刚进行了谷歌搜索,这个页面看起来在解释为什么需要它方面做得很好。 http://fvue.nl/wiki/Bash:_Why_use_eval_with_variable_expansion%3F
回答by Jason Catena
There is a point to only put commands and options in variables.
有一点只将命令和选项放在变量中。
##代码##You can relocate the commands to another file you source, so you can reuse the same commands and options across many scripts. This is very handy when you have a lot of scripts and you want to control how they all use tools. So standard_tools would contain:
您可以将命令重新定位到您提供的另一个文件,这样您就可以在多个脚本中重复使用相同的命令和选项。当您有很多脚本并且您想控制它们如何使用工具时,这非常方便。所以standard_tools 将包含:
##代码##回答by Tanktalus
Quoting spaces inside variables such that the shell will re-interpret things properly is hard. It's this type of thing that prompts me to reach for a stronger language. Whether that's perl or python or ruby or whatever (I choose perl, but that's not always for everyone), it's just somethingthat will allow you to bypass the shell for quoting.
引用变量中的空格以便 shell 正确地重新解释事物是很困难的。正是这种类型的事情促使我去寻找一种更强大的语言。不管是Perl或Python或Ruby或什么(我选择perl的,但是这并不总是给大家),它只是东西,可以让你绕过外壳的报价。
It's not that I've never managed to get it right with liberal doses of eval, but just that eval gives me the eebie-jeebies (becomes a whole new headache when you want to take user input and eval it, though in this case you'd be taking stuff that you wrote and evaling that instead), and that I've gotten headaches in debugging.
并不是说我从来没有设法用自由剂量的 eval 把它做好,只是那个 eval 给了我 eebie-jeebies(当你想接受用户输入并评估它时,这成为一个全新的头痛,尽管在这种情况下你我会拿你写的东西来代替评估),而且我在调试时很头疼。
With perl, as my example, I'd be able to do something like:
以 perl 为例,我可以执行以下操作:
##代码##The hard part here is doing the pipes - but a bit of IO::Pipe, fork, and reopening stdout and stderr, and it's not bad. Some would say that's worse than quoting the shell properly, and I understand where they're coming from, but, for me, this is easier to read, maintain, and write. Heck, someone could take the hard work out of this and create a IO::Pipeline module and make the whole thing trivial ;-)
这里最难的部分是做管道——但是有点IO::Pipe,fork,并重新打开 stdout 和 stderr,这还不错。有人会说这比正确引用 shell 更糟糕,我理解它们的来源,但是,对我来说,这更易于阅读、维护和编写。哎呀,有人可以把辛苦的工作排除在外,创建一个 IO::Pipeline 模块,让整个事情变得微不足道;-)