首页 > 解决方案 > 在需要在单引号内包含反引号的命令中替换变量

问题描述

我试图编写一个简单的 bash 脚本来使用现有问题中建议的脚本自动删除 30 天以上的 EBS 快照删除早于一个月的 AWS EC2 快照

但是,我无法让它正确运行或将日期作为变量获取:

原始代码如下所示:

snapshots_to_delete=($(aws ec2 describe-snapshots --owner-ids xxxxxxxxxxxx --query 'Snapshots[?StartTime>=`2017-02-15`].SnapshotId' --output text))

我希望它运行类似:

DATE=`date --date="3 month ago" +%Y-%m-%d`

snapshots_to_delete=($(aws ec2 describe-snapshots --owner-ids xxxxxxxxxxxx --query 'Snapshots[?StartTime>=$DATE].SnapshotId' --output text))

我已经尝试了 , , , , 的所有组合["并且\/`想到'让它工作,但到目前为止还没有运气!

标签: bashaws-cli

解决方案


解决此问题的一种简单方法是在需要文字的反引号之后结束单引号,将您的扩展置于双引号扩展中,然后为命令的其余部分切换回单引号上下文:

date=$(date --date="3 month ago" +%Y-%m-%d)

IFS=$'\n' read -r -d '' -a snapshots_to_delete < <(
  aws ec2 describe-snapshots \
    --owner-ids xxxxxxxxxxxx \
    --query 'Snapshots[?StartTime>=`'"$date"'`].SnapshotId' && printf '\0'
)
declare -p snapshots_to_delete >&2 # print the resulting value

为什么要这样写?

  • 'Snapshots[?StartTime>=`'"$date"'`].SnapshotId'中,有三个不同的子字符串,每个子字符串都以不同的方式引用,语法引号字符在引用样式之间转换:

    1. 第一个字符 ,'开始一个单引号上下文;它是 shell 语法,而不是数据。
    2. Snapshots[?StartTime>=`是第一个子串;因为它在单引号内,所以不会以任何方式修改(并且反引号不会被视为特殊的 shell),因此aws完全按原样成为命令参数向量中字符串的一部分。
    3. 下一个字符 another'结束单引号上下文;它也是 shell 语法,而不是数据。
    4. 下一个字符 ,"是 shell 语法,它启动一个双引号上下文。这是因为在双引号上下文中,保证参数扩展不会进行字符串拆分或全局扩展。
    5. $date,在双引号上下文中,是扩展为包含子字符串的第二个段 - 替换为命名变量的值。这种扩展的结果,在双引号中,被视为没有应用进一步解析步骤的数据。
    6. 接下来"是结束双引号上下文的语法。
    7. 接下来'是开始一个新的单引号上下文的语法。
    8. `].SnapshotId,被视为数据的第三个子字符串,在该单引号上下文中作为文字给出;由于该上下文,反引号文字aws按原样传递。
    9. 最后'是结束单引号上下文的语法。
  • 使用小写的变量名称date符合 POSIX 指南,该指南指定全大写名称用于对 shell 和 OS 提供的工具有意义的变量,而保留至少一个字符的名称供应用程序使用。请参阅http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html,记住 shell 和环境变量共享一个命名空间。

  • 替换snapshots_to_delete=( $(...) )是出于BashPitfalls #50中描述的原因:
    • UsingIFS=$'\n' read -r -d '' -a snapshots_to_delete导致 stdin 在换行符处从流中拆分为数组元素,该流预计以单个 NUL 字节结尾。如果该 NUL 字节不存在,read将以非零状态退出(指示错误)。
    • 如果命令成功,则放在命令&& printf '\0'末尾aws会导致发出 NUL 字节。通过这种方式,我们确保将成功或失败的信息传播回命令;这也可以移植到旧版本的 bash,而不是支持替代或命令。awsawsreadreadarraymapfile
    • $( ... )作为命令替换,比反引号更受欢迎,因为反引号会更改反斜杠和其中其他反引号的含义,除非包含的内容更改其引用。见http://wiki.bash-hackers.org/syntax/expansion/cmdsubst#a_closer_look_at_the_two_forms
  • 使用read -r -a < <(...)而不是... | read -r -a出于BashFAQ #24中所述的原因。有问题的语法也称为“进程替换”,记录在http://wiki.bash-hackers.org/syntax/expansion/proc_subst

推荐阅读