Можно ли кэшировать вывод команды в Linux из CLI?
Я ищу реализацию команды "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
Этот пример немного глуп, потому что он не имеет выхода. В идеале это было бы протестировано на script подобно sleep1-and-echo-hello-world.sh.
Я создал небольшой script, который создает файл в/tmp/с hash с полным именем команды и именем пользователя, но я уверен, что что-то уже существует.
Знаете ли вы об этом?
Ответы
Ответ 1
Улучшенное решение выше, а также добавление возраста истечения в качестве необязательного аргумента.
#!/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="[email protected]"
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}"
Ответ 2
Как насчет этой простой оболочки script (не тестировался)?
#!/bin/sh
mkdir -p cache
cachefile=cache/cache
for i in "[email protected]"
do
cachefile=${cachefile}_$(printf %s "$i" | sed 's/./\\&/g')
done
test -f "$cachefile" || "[email protected]" > "$cachefile"
cat "$cachefile"
Ответ 3
Решение, которое я выбрал в рубине, - это. Кто-нибудь видит оптимизацию?
#!/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
Ответ 4
Я реализовал простое кэширование script для bash, потому что я хотел ускорить построение графика из командной строки оболочки в gnuplot. Он может использоваться для кэширования вывода любой команды. Кэш используется до тех пор, пока аргументы одинаковы, а файлы, переданные в аргументах, не изменились. Система отвечает за очистку.
#!/bin/bash
# hash all arguments
KEY="[email protected]"
# hash last modified dates of any files
for arg in "[email protected]"
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
[email protected] | tee $FILE
fi
Вы можете назвать script cache
, установить исполняемый флаг и поместить его в PATH
. Затем просто прикрепите любую команду с помощью cache
, чтобы использовать ее.
Ответ 5
Реализация существует здесь: https://bitbucket.org/sivann/runcached/src
Кэширует исполняемый путь, выводит, выводит код, запоминает аргументы. Настраиваемое выключение. Реализовано в bash, C, python, выберите то, что вам подходит.
Ответ 6
Я создал утилиту для напоминания для Bash, которая работает именно так, как вы описываете. Он разработан специально для кэширования функций Bash, но, очевидно, вы можете заключать вызовы других команд в функции.
Он обрабатывает ряд крайних вариантов поведения, которые пропускают многие простые механизмы кэширования. Он сообщает код завершения исходного вызова, хранит stdout и stderr отдельно и сохраняет все конечные пробелы в выходных данных (подстановки команд $()
усекают конечные пробелы).
Демо-версия:
# Define function normally, then decorate it with bc::cache
$ maybe_sleep() {
sleep "[email protected]"
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 секунды. Эта утилита поможет вам решить, стоит ли кэшировать тот или иной вызов или нет.
Ответ 7
Улучшено после устранения ошибки:
- Трубы выводятся в команду "tee", которая позволяет просматривать их в реальном времени, а также сохранять в кеше.
- Сохраните цвета (например, в таких командах, как "ls --color"), используя "script --flush --quiet/dev/null --command $ CMD".
- Избегайте вызова "exec", используя также скрипт
- Используйте bash и [[
#!/usr/bin/env bash
CMD="[email protected]"
[[ -z $CMD ]] && echo "usage: EXPIRY=600 cache cmd arg1 ... argN" && exit 1
# set -e -x
VERBOSE=false
PROG="$(basename $0)"
EXPIRY=${EXPIRY:-600} # default to 10 minutes, can be overriden
EXPIRE_DATE=$(date -Is -d "-$EXPIRY seconds")
[[ $VERBOSE = true ]] && echo "Using expiration $EXPIRY seconds"
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 ]]; then
cat "$CACHEFILE"
else
script --flush --quiet --return /dev/null --command "$CMD" | tee "$CACHEFILE"
fi