代码之家  ›  专栏  ›  技术社区  ›  wxs

bash脚本中变量作为命令

  •  20
  • wxs  · 技术社区  · 16 年前

    我正在编写一个非常简单的bash脚本,该脚本对给定的目录进行tar,对该目录的输出进行加密,然后将结果文件拆分为多个较小的文件,因为备份媒体不支持大文件。

    我对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 $0` DIRECTORY BACKUP_DIRECTORY"
        exit 1
    fi
    
    DIRECTORY=$1
    BACKUP_DIRECTORY=$2
    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"
    

    运行此命令失败,原因是:

    拆分:“foo/2009-04-27t14-32-04.backup”aa:没有这样的文件或目录

    我可以去掉它周围的引号 $BACKUP_FILE 我设置的地方 $SPLIT_CMD . 但是,如果我在备份目录的名称中有一个空格,它就不起作用。另外,如果我直接将“echo”命令的输出复制并粘贴到终端,它也可以正常工作。很明显,我不明白巴什是如何逃避事情的。

    5 回复  |  直到 8 年前
        1
  •  42
  •   OrangeDog    10 年前

    不要把整个命令放在变量中。你在试图恢复引用的论点时会遇到很多麻烦。

    也:

    1. 避免在脚本中使用所有大写的变量名。用简单的方法射自己的脚。
    2. 不要使用反引号,使用$(…)代替,它嵌套得更好。

    #! /bin/bash
    
    if [ $# -ne 2 ]
    then
        echo "Usage: $(basename $0) DIRECTORY BACKUP_DIRECTORY"
        exit 1
    fi
    
    directory=$1
    backup_directory=$2
    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"
    
        2
  •  6
  •   Eddie    16 年前

    我不确定,但可能值得先对命令运行eval。

    这将允许bash将变量$tar_cmd等扩展到它们的整个范围(就像echo命令对控制台所做的那样,您认为它是有效的)

    然后bash将在变量展开的情况下再次读取该行。

    eval $TAR_CMD | $ENCRYPT_CMD | $SPLIT_CMD 
    

    我刚刚做了一个谷歌搜索,这个页面看起来可以很好地解释为什么需要这样做。 http://fvue.nl/wiki/Bash:_Why_use_eval_with_variable_expansion%3F

        3
  •  6
  •   Charles Duffy    9 年前

    eval 如果目录名可以由不受信任的源生成,则不是可接受的做法。见 BashFAQ #48 了解更多原因 埃瓦 不应使用,并且 BashFAQ #50 有关此问题的根本原因及其正确解决方案的详细信息,请参阅以下部分内容:

    如果需要随着时间的推移建立命令,请使用数组:

    tar_cmd=( tar cv "$directory" )
    split_cmd=( split -b 1024m - "$backup_file" )
    encrypt_cmd=( openssl des3 -salt )
    "${tar_cmd[@]}" | "${encrypt_cmd[@]}" | "${split_cmd[@]}"
    

    或者,如果这只是在一个中心位置定义命令,请使用函数:

    tar_cmd() { tar cv "$directory"; }
    split_cmd() { split -b 1024m - "$backup_file"; }
    encrypt_cmd() { openssl des3 -salt; }
    tar_cmd | split_cmd | encrypt_cmd
    
        4
  •  4
  •   Jason Catena    16 年前

    有一点是只把命令和选项放在变量中。

    #! /bin/bash
    
    if [ $# -ne 2 ]
    then
        echo "Usage: `basename $0` DIRECTORY BACKUP_DIRECTORY"
        exit 1
    fi
    
    . standard_tools    
    
    directory=$1
    backup_directory=$2
    current_date=$(date +%Y-%m-%dT%H-%M-%S)
    backup_file="${backup_directory}/${current_date}.backup"
    
    ${tar_create} "${directory}" | ${openssl} | ${split_1024} "$backup_file"
    

    您可以将命令重新定位到源文件中的另一个文件,以便在许多脚本中重用相同的命令和选项。当你有很多脚本并且你想控制它们如何使用工具的时候,这是非常方便的。因此,标准工具将包含:

    export tar_create="tar cv"
    export openssl="openssl des3 -salt"
    export split_1024="split -b 1024m -"
    
        5
  •  1
  •   Tanktalus    8 年前

    引用变量中的空格,以便shell能够正确地重新解释 坚硬的 . 正是这种类型的事情促使我去寻求一种更强的语言。不管是perl、python、ruby还是其他什么(我选择perl,但这并不总是适合所有人),它只是 某物 这样你就可以绕过壳牌公司报价了。

    并不是说我从来没有尝试过用大量的eval来实现它,而是eval给了我eebie jeebies(当你想要接受用户输入并对其进行eval时,会成为一个全新的头痛,尽管在这种情况下,你需要的是你写的东西并对其进行eval),我已经去了调试时有点麻烦。

    以perl为例,我可以做如下事情:

    @tar_cmd = ( qw(tar cv), $directory );
    @encrypt_cmd = ( qw(openssl des3 -salt) );
    @split_cmd = ( qw(split -b 1024m -), $backup_file );
    

    这里最困难的是做管道-但是 IO::Pipe ,fork,然后重新打开stdout和stderr,这还不错。有人会说,这比正确引用shell更糟糕,我知道它们来自何方,但对我来说,这更容易阅读、维护和编写。见鬼,有人可以把这些辛苦的工作去掉,创建一个io::pipeline模块,使整个事情变得微不足道;-)