bash 是否有可用的内置缓存命令(有点像 mktemp 或 Sponge)?

bash 是否有可用的内置缓存命令(有点像 mktemp 或 Sponge)?

我正在使用 amazon ec2 命令行工具,该ec2-describe-instances工具有点痛苦,因为它需要 2-5 秒来发出请求并显示输出。

我正在考虑使用fec2din中描述的工具这个问题格式化输出ec2-describe-instances,想知道缓存调用输出的最佳方法是什么。

fec2din用于mktemp创建临时文件,然后用于awk格式化输出。

是否有一些工具可以与 TTL 参数一起使用,只有ec2-describe-instances在缓存文件上的时间戳早于某个时间时才会运行?

如果有一些实用程序可以做到这一点(就像sponge帮助一样stdout),那就太好了。

答案1

虽然这并不能解决您的问题,但我强烈建议您使用亚马逊EC2通过优秀的博托相反,这是一个提供 Amazon Web Services 接口的 Python 包

它几乎覆盖了与Amazon EC2 API 工具,但不会因为依赖现代且快速的 AWS REST API 而遭受痛苦的延迟,而EC2 API 工具都是用 Java 编写的,并且习惯于使用旧的、缓慢的 SOAP API(不知道他们是否已经在这方面改变了方向,但是您的经验以及仍然需要AWS X.509 证书似乎另有建议)。

此外,您不需要使用这些AWS X.509 证书不再,而是可以通过以下方式使用当今更常见和灵活的方法AWS 访问密钥 IDAWS 秘密访问密钥,也可以(通常应该)通过提供AWS 身份和访问管理 (IAM)以避免暴露您的主 AWS 账户凭证。

最重要的是,博托它显然是通过 Python 脚本编排您日常 AWS 使用的候选者 - 当然也可以这样做bash,但您明白了;)

文档

您可以在以下位置找到文档和示例boto:Amazon Web Services 的 Python 接口,它提供了体面的(即或多或少完整)API 参考(例如EC2)以及专门的介绍性文章解释了几种服务(但还不是全部)的基本用法,例如boto 的 EC2 接口简介涵盖手头的用例。

此外,您可能还想阅读博托配置用于设置您的环境(凭据等)。

用法

您可以探索博托通过Python读取-评估-打印循环 (REPL),即通过启动python.

一旦您对片段感到满意,您可以将它们转换为 Python 脚本以供独立使用。

例子

这是一个大约解决您的用例的示例(假设您已在您的环境中设置了凭据,如中所述博托配置):

$ python
Python 2.7.2 (default, Jun 12 2011, 14:24:46)
Type "help", "copyright", "credits" or "license" for more information.
>>> import boto
>>> ec2 = boto.connect_ec2()
>>> instances = ec2.get_all_instances()
>>> instances
[Reservation:r-916d01f2, Reservation:r-3f7e055c, Reservation:r-c37209a0]

好吧,get_all_instances()实际上返回了一个列表boto.ec2.instance.Reservation,所以这里有一个恼人的间接寻址(源于 EC2 API),您通常不会在其他地方看到它 - 文档已经是结论性的,但让我们看看如何通过内省找出它:

>>> dir(instances[0])
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', 
'__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', 
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 
'connection', 'endElement', 'groups', 'id', 'instances', 'item', 'owner_id', 
'region', 'startElement', 'stop_all']
>>> insts = instances[0].instances
>>> insts
[Instance:i-5d9a593a]

这更像是这样,所以最后您想查看以下属性值i-5d9a593a(为了简洁和隐私,省略了大多数属性):

>>> vars(insts[0])
{'kernel': u'aki-825ea7eb', 'private_dns_name': '', 'id': u'i-5d9a593a', 
'monitored': False, 'state': u'stopped', 'architecture': u'x86_64',  
 'public_dns_name': '', 'ip_address': None, 'placement': u
'us-east-1a', 'ami_launch_index': u'0', 'dns_name': '', 'region': RegionInfo:us-east-1
# ...
}

不完全是,但Python的数据漂亮打印机(pprint)救援:

>>> import pprint
>>> pp = pprint.PrettyPrinter(indent=4)
>>> pp.pprint(vars(insts[0])) {   
    '_in_monitoring_element': False,
    'ami_launch_index': u'0',
    'architecture': u'x86_64',
    'dns_name': '',
    'hypervisor': u'xen',
    'id': u'i-5d9a593a',
    'instance_class': None,
    'instance_type': u'm1.medium',
    'ip_address': None,
    'kernel': u'aki-825ea7eb'
    # ...
    }

答案2

sponge可以通过将行读入数组然后输出来制作临时实用程序,例如:

sponge() {
    local line lines
    while IFS= read -r line; do
        lines+=( "$line" )
    done
    printf '%s\n' "${lines[@]}"
}

然后将其运行为command1 | sponge | command2.

答案3

这是 bash/ksh/zsh 的一个非常粗糙的缓存工具。

typeset -A output_cache
cache () {
  local IFS='
  ' ret=0
  if [[ -z ${output_cache["$*"]} ]]; then
    output_cache["$*"]=$(unset IFS; "$@")
    ret=$?
  fi
  echo "${output_cache["$*"]}"
  return $ret
}
uncache () {
  local IFS='
  '
  unset output_cache["$*"]
}

例子:

$ cache mycommand --options        # takes a while
$ cache mycommand --options        # instantaneous
$ uncache mycommand --options      # remove a cache entry

限制:

  • 仅当运行该命令两次每次产生相同的输出时,这才有意义。
  • 缓存不在 shell 实例之间共享。
  • 这仅适用于简单命令,不适用于管道、循环等(除非您使用cache sh -c …)。
  • 不区分仅因字分隔而不同的高速缓存条目(例如mycommand "foo bar"vs. )。mycommand "foo" "bar"这在实践中不应该是一个真正的问题。
  • 该命令的第一次调用返回其返回码。缓存的调用返回 0。如果要返回原始返回码,请添加关联数组来存储返回码。另一种可能的行为是仅当命令返回非零状态时才将命令输出存储在缓存中。
  • 仅保存标准输出,不保存标准错误或其他文件描述符的输出。
  • 空输出不被视为缓存。
  • 输出末尾的换行符数量标准化为 1。(这很容易修复,但在实践中这种方式可能更好。)
  • 后不能使用别名cache。这云有一线希望:如果您发现总是想缓存命令,可以将其别名为cache mycommand.
  • 没有期限啊有许多潜在有用的过期机制(尝试检测相关性、保留最大数量的条目、为条目提供生存时间……),我不会将它们作为此答案的一部分来实现。

答案4

#!/bin/sh
PROG="$(basename $0)"
DIR="${HOME}/.cache/${PROG}"
mkdir -p "${DIR}"
EXPIRY=600 # default to 10 minutes
[ "$1" -eq "$1" ] 2>/dev/null && EXPIRY=$1 && shift
CMD="$@"
HASH=$(echo "$CMD" | md5sum | awk '{print $1}')
CACHE="$DIR/$HASH"
test -f "${CACHE}" && [ $(expr $(date +%s) - $(date -r "$CACHE" +%s)) -le $EXPIRY ] || "$CMD" > "${CACHE}"
cat "${CACHE}"

相关内容