11

我正在寻找一个' cacheme '命令的实现,它' memoizes ' ARGV 中任何内容的输出。如果它从未运行过它,它将运行它并在一定程度上记住输出。如果它运行它,它只会复制文件的输出(或者更好的是,将输出和错误分别复制到 &1 和 &2)。

假设有人写了这个命令,它会像这样工作。

$ time cacheme sleep 1    # first time it takes one sec
real   0m1.228s
user   0m0.140s
sys    0m0.040s

$ time cacheme sleep 1    # second time it looks for stdout in the cache (dflt expires in 1h)
#DEBUG# Cache version found! (1 minute old)

real   0m0.100s
user   0m0.100s
sys    0m0.040s

这个例子有点傻,因为它没有输出。理想情况下,它将在sleep-1-and-echo-hello-world.sh 之类的脚本上进行测试。

我创建了一个小脚本,它在 /tmp/ 中创建一个文件,其中包含完整的命令名称和用户名的哈希值,但我很确定某些东西已经存在。

你知道这些吗?

4

8 回答 8

7

通过添加到期期限作为可选参数,在某种程度上改进了上述解决方案。

#!/bin/sh
# save as e.g. $HOME/.local/bin/cacheme
# and then chmod u+x $HOME/.local/bin/cacheme
VERBOSE=false
PROG="$(basename $0)"
DIR="${HOME}/.cache/${PROG}"
mkdir -p "${DIR}"
EXPIRY=600 # default to 10 minutes
# check if first argument is a number, if so use it as expiration (seconds)
[ "$1" -eq "$1" ] 2>/dev/null && EXPIRY=$1 && shift
[ "$VERBOSE" = true ] && echo "Using expiration $EXPIRY seconds"
CMD="$@"
HASH=$(echo "$CMD" | md5sum | awk '{print $1}')
CACHE="$DIR/$HASH"
test -f "${CACHE}" && [ $(expr $(date +%s) - $(date -r "$CACHE" +%s)) -le $EXPIRY ] || eval "$CMD" > "${CACHE}"
cat "${CACHE}"
于 2016-01-03T06:02:38.293 回答
5

我已经为 bash 实现了一个简单的缓存脚本,因为我想加快 gnuplot 中管道 shell 命令的绘图速度。它可用于缓存任何命令的输出。只要参数相同并且传入参数的文件没有更改,就会使用缓存。系统负责清理。

#!/bin/bash

# hash all arguments
KEY="$@"

# hash last modified dates of any files
for arg in "$@"
do
  if [ -f $arg ]
  then
    KEY+=`date -r "$arg" +\ %s`
  fi
done

# use the hash as a name for temporary file
FILE="/tmp/command_cache.`echo -n "$KEY" | md5sum | cut -c -10`"

# use cached file or execute the command and cache it
if [ -f $FILE ]
then
  cat $FILE
else
  $@ | tee $FILE
fi

您可以命名脚本cache,设置可执行标志并将其放入您的PATH. 然后只需在任何命令前加上前缀cache即可使用它。

于 2016-03-20T12:33:20.563 回答
2

我创建了bash-cache,这是一个用于 Bash 的记忆库,它的工作原理与您所描述的完全一样。它是专门为缓存 Bash 函数而设计的,但显然你可以在函数中包装对其他命令的调用。

它处理许多更简单的缓存机制遗漏的一些边缘情况行为。它报告原始调用的退出代码,分别保留 stdout 和 stderr,并在输出中保留任何尾随空格($()命令替换将截断尾随空格)。

演示:

# Define function normally, then decorate it with bc::cache
$ maybe_sleep() {
  sleep "$@"
  echo "Did I sleep?"
} && bc::cache maybe_sleep

# Initial call invokes the function
$ time maybe_sleep 1
Did I sleep?

real    0m1.047s
user    0m0.000s
sys     0m0.020s

# Subsequent call uses the cache
$ time maybe_sleep 1
Did I sleep?

real    0m0.044s
user    0m0.000s
sys     0m0.010s

# Invocations with different arguments are cached separately
$ time maybe_sleep 2
Did I sleep?

real    0m2.049s
user    0m0.000s
sys     0m0.020s

还有一个基准函数显示缓存的开销:

$ bc::benchmark maybe_sleep 1
Original:       1.007
Cold Cache:     1.052
Warm Cache:     0.044

所以你可以看到读/写开销(在我的机器上,它使用tmpfs)大约是 1/20 秒。此基准实用程序可以帮助您确定是否值得缓存特定调用。

于 2018-05-09T16:16:50.573 回答
1

这个简单的 shell 脚本(未测试)怎么样?

#!/bin/sh

mkdir -p cache

cachefile=cache/cache

for i in "$@"
do
    cachefile=${cachefile}_$(printf %s "$i" | sed 's/./\\&/g')
done

test -f "$cachefile" || "$@" > "$cachefile"
cat "$cachefile"
于 2012-08-10T12:05:02.663 回答
1

改进了从错误的解决方案

  • 管道输出到“tee”命令,允许实时查看以及存储在缓存中。
  • 使用“script --flush --quiet /dev/null --command $CMD”保留颜色(例如在“ls --color”等命令中)。
  • 避免使用脚本调用“exec”
  • 使用 bash 和 [[
    #!/usr/bin/env bash

    CMD="$@"
    [[ -z $CMD ]] && echo "usage: EXPIRY=600 cache cmd arg1 ... argN" && exit 1

    # 设置-e -x

    详细=假
    PROG="$(basename $0)"

    EXPIRY=${EXPIRY:-600} # 默认为10分钟,可以覆盖
    EXPIRE_DATE=$(日期 -Is -d "-$EXPIRY seconds")

    [[ $VERBOSE = true ]] && echo "使用过期 $EXPIRY 秒"

    HASH=$(echo "$CMD" | md5sum | awk '{print $1}')
    CACHEDIR="${HOME}/.cache/${PROG}"
    mkdir -p "${CACHEDIR}"
    CACHEFILE="$CACHEDIR/$HASH"

    if [[ -e $CACHEFILE ]] && [[ $(date -Is -r "$CACHEFILE") > $EXPIRE_DATE ]]; 然后
        猫“$CACHEFILE”
    别的
        脚本 --flush --quiet --return /dev/null --command "$CMD" | 三通“$CACHEFILE”
    菲
于 2019-02-05T16:14:47.693 回答
1

bash-cache的作者在这里进行了更新。我最近发布bkt了一个用于子进程缓存的 CLI 和 Rust 库。这是一个简单的例子:

# Execute and cache an invocation of 'date +%s.%N'
$ bkt -- date +%s.%N
1631992417.080884000

# A subsequent invocation reuses the same cached output
$ bkt -- date +%s.%N
1631992417.080884000

它支持许多功能,例如异步刷新 (--stale--warm)、命名空间缓存 ( --scope),以及可选地关闭工作目录 ( --cwd) 和选择环境变量 ( --env)。有关更多信息,请参阅自述文件。

它仍在进行中,但功能强大且有效!我已经在使用它来加速我的 shell 提示符和一些其他常见任务。

于 2021-11-19T07:25:32.260 回答
0

我在红宝石中提出的解决方案是这样的。有人看到任何优化吗?

#!/usr/bin/env ruby

VER = '1.2'
$time_cache_secs = 3600
$cache_dir = File.expand_path("~/.cacheme")

require 'rubygems'
begin
  require 'filecache'           # gem install ruby-cache
rescue Exception => e
  puts 'gem filecache requires installation, sorry. trying to install myself'
  system  'sudo gem install -r filecache'
  puts  'Try re-running the program now.'
  exit 1
end

=begin
  # create a new cache called "my-cache", rooted in /home/simon/caches
  # with an expiry time of 30 seconds, and a file hierarchy three
  # directories deep
=end
def main
  cache = FileCache.new("cache3", $cache_dir, $time_cache_secs, 3)
  cmd = ARGV.join(' ').to_s   # caching on full command, note that quotes are stripped
  cmd = 'echo give me an argment' if cmd.length < 1

  # caches the command and retrieves it
  if cache.get('output' + cmd)
    #deb "Cache found!(for '#{cmd}')"
  else
    #deb "Cache not found! Recalculating and setting for the future"
    cache.set('output' + cmd, `#{cmd}`)
  end
  #deb 'anyway calling the cache now'
  print(cache.get('output' + cmd))
end

main
于 2012-08-10T11:07:07.607 回答
0

这里有一个实现:https : //bitbucket.org/sivann/runcached/src 缓存可执行路径、输出、退出代码,记住参数。可配置过期。用 bash、C、python 实现,选择适合你的。

于 2016-10-28T09:01:09.993 回答