Есть ли команда оболочки или служебная программа для кэширования вывода процесса?

Это *:

for i in `echo '*'`; do echo "$i"; done

Как его найти:

for i in `pwgen -yB -N 100 1 | tee /tmp/f1`; do echo "$i"; done >/tmp/f2
diff f1 f2

Возможно, вам придется запустить его несколько раз, пока вы не увидите разницу. Или увеличить N.

3
03.01.2017, 16:00
9 ответов

Вы можете записать результат в файл а затем прочитать его обратно из этого файла ...

tmpDir=/tmp/$$
rm -rf "$tmpDir"
mkdir "$tmpDir"

echo cmd1 > "$tmpDir"/cmd1_stdout 2> "$tmpDir"/cmd1_stderr
echo $? > "$tmpDir"/cmd1_exitcode

# Retrieving output of cmd1:
( cat "$tmpDir"/cmd1_stdout ; cat "$tmpDir"/cmd1_stderr 1>&2; exit $(cat "$tmpDir"/cmd1_exitcode) )

Отсюда мы можем определить функцию "кеширования". В этой версии нужен символ, который мы никогда не будем использовать в качестве аргумента. Например, запятая ",". Вы можете изменить его в строке «IFS =»

tmpDir=/tmp/$$
rm -rf "$tmpDir"
mkdir "$tmpDir"

cache() {

 IFS=, cmd="$*"
 if [ -f "$tmpDir/$cmd"_exitcode ]; then 
   cat "$tmpDir/$cmd"_stdout
   cat "$tmpDir/$cmd"_stderr 1>&2
   return $(cat "$tmpDir"/cmd1_exitcode)
 fi

   # This line is bash-only:
 "$@" 2> >(tee "$tmpDir/$cmd"_stderr 1>&2) > >(tee "$tmpDir/$cmd"_stdout)
 local e=$?
 echo $e > "$tmpDir/$cmd"_exitcode

 return $e
}

Тайм-аут может быть реализован с помощью «date +% s» и «stat -c% Y»:

tmpDir=/tmp/$$
rm -rf "$tmpDir"
mkdir "$tmpDir"

cache() {

 local timeout=$1
 shift

 IFS=, cmd="$*"
 if [ -f "$tmpDir/$cmd"_exitcode ]; then 

   local now=$(date +%s)
   local fdate=$(stat -c %Y "$tmpDir/$cmd"_exitcode)

   if [ $((now-fdate)) -le $timeout ]; then 
     cat "$tmpDir/$cmd"_stdout
     cat "$tmpDir/$cmd"_stderr 1>&2
     return $(cat "$tmpDir/$cmd"_exitcode)
   fi

 fi

   # This line is bash-only:
 "$@" 2> >(tee "$tmpDir/$cmd"_stderr 1>&2) > >(tee "$tmpDir/$cmd"_stdout)
 local e=$?
 echo $e > "$tmpDir/$cmd"_exitcode

 return $e
}

Строку «bash only» можно заменить на:

  "$@" 2> "$tmpDir/$cmd"_stderr > "$tmpDir/$cmd"_stdout
  local e=$?
  cat "$tmpDir/$cmd"_stdout
  cat "$tmpDir/$cmd"_stderr 1>&2
1
27.01.2020, 21:30

Наиболее распространенный механизм кэширования вывода команд в сценариях оболочки - присвоение его переменной. Это легко сделать с подоболочкой. Срок его действия не истекает, как у традиционного кеша, но те, кто пишет сценарии оболочки, чаще всего находят его приемлемым. Вот ваш приведенный выше сценарий с использованием подоболочки и переменной

HI=$(echo hi)
echo $HI
sleep 2
echo $HI
sleep 10
echo $HI

Другой альтернативой является создание функции кеширования в сценарии оболочки. Что-то вроде ...

cache() {
expiry=$1
cmd=$2-
cache=/tmp/{$2-}_cache

if test "`find -not -newermt '-30 seconds' -delete ${cache}`"; then
$cmd |tee "$cache"
else
cat "$cache"
fi
}
0
27.01.2020, 21:30

Насколько мне известно, не существует стандартного или традиционного инструмента, который работает таким образом. Однако, поиск "cache stdout" привел меня к https://github.com/Valiev/cache, который, похоже, является инструментом, который делает то, что вы хотите.

0
27.01.2020, 21:30

Я не знаю общего инструмента для такого рода кэширования, но я знаю специфический инструмент для кэширования результатов компиляции файлов C или C++, ccache compiler cache.

Он хранит предварительно обработанные исходные тексты и скомпилированные файлы объектного кода и использует хэши на основе исходного кода и сигнатуры компилятора, чтобы определить, следует ли использовать кэш или перекомпилировать файл.

Общий кэш для shell должен будет аналогичным образом хэшировать сценарий, сигнатуру каждой используемой внешней утилиты и т.д., а также учитывать, что вывод сценария shell может зависеть от времени суток (если он использует дату) и многих многих других факторов.

Создание кэша для одной команды не принесет пользы, особенно для echo, поскольку он встроен в большинство оболочек и дополнительная обработка его для кэширования только замедлит его (если только то, что было эхом, не вызывало также тяжелые внешние утилиты через подстановки команд и т.д.). )

Я не думаю, что в конечном итоге это было бы целесообразно, но, возможно, полезно только в очень специфическом контексте, как, например, утилита ccache для кэширования компиляций, которая представляет собой проблему, несколько более ограниченную в своем масштабе.

Также обратите внимание, что хотя утилита может выдавать некоторый результат, она может иметь побочные эффекты (например, запись в файлы и т.д.), которые нелегко кэшировать.

0
27.01.2020, 21:30

используйте эту командуhttps://github.com/jingminglang/cmd_cache

git-клонhttps://github.com/jingminglang/cmd_cache

постройте main.go

./main -c [ваша команда] -t [время кеша]

-1
27.01.2020, 21:30

Я не могу оставить комментарий, поэтому я просто создам новый пост.

Проблема анализирует

Насколько мне известно, нет команды оболочки (, являющейся частью исправления bash ), которая используется для кэширования данных... однако эта доза не означает, что нет средств для ее реализации..

Как бы вы ни хотели реализовать это, полностью зависит от среды вашего дистрибутива и от того, на какой файловой структуре она построена. Стандарт иерархии файловой системы (вики)

Перейдите к разделу о соответствии FHS, и он в основном скажет вам, что не все дистрибутивы Linux созданы одинаково (case и укажите разные версии оболочки в разных дистрибутивах, также известные как bourn shell и kornshell )... В Сомме может быть /dev/shm В Сомме может не быть... Вот почему вы получили сообщение:

ls: /dev/shm: No such file or directory

Очевидно, что ваш дистрибутив без файла /dev/shm !!!


Хотя большинство:

Most Linux distributions follow the Filesystem Hierarchy Standard and declare it their own policy to maintain FHS compliance.

Вывод?!

Итак, теперь мой вопрос, как решить эту головоломку? ну я бы сказал, довольно просто...

Создать список всех потенциальных каталогов, которые хранятся в памяти, а не на жестком диске. и создайте условие if elif, в котором вы проверяете, существует ли файл или нет. Должен ли существовать один из файлов (, что больше, чем likley, если они не изменили структуру файла )Затем вы можете просто объявить переменную с именем temp и заполнить ее путем временной файловой системы tmpfs ()

Чтобы дать вам общее представление

#!/bin/bash                                                          

#/run directory as a temporary filesystem (tmpfs) 
#which stores volatile runtime data, following the FHS version 3.0.
if [ -e /run ]
    then
        temp='/run'
# with FHS version 2.3, such data were stored in /var/run
#for compatibility there might be a simlink
elif [ -e /var/run ]
    then
        temp='/var/run'
elif [ -e /dev/shm/]
    then
        temp='/var/shm'
elif [ -e /var/tmp ]
    then
        #this should not occur with the current fhs standerts
        #switching to /var/tmp just in case ;)
        temp='/var/tmp'
fi
echo $temp

Это просто для того, чтобы дать вам основную идею(Я все еще работаю над этим сам (для моего окончательного проекта;)

Дополнительно но я считаю, что это может быть полезно для других. Теперь вы можете просто создать файл в каталоге с именем процесса, за которым следует отметка времени, чтобы создать задание cron для автоматической очистки ваших временных заполнений через регулярные промежутки времени или просто вызовом команды rm, когда вы не файл больше не нужен.

PS Если вы найдете какие-либо дополнительные просьбы tmpfs, просто отредактируйте этот пост. Спасибо.

0
27.01.2020, 21:30

Я только что написал довольно полный сценарий для этого; последняя версия находится по адресуhttps://gist.github.com/akorn/51ee2fe7d36fa139723c851d87e56096.

#!/bin/zsh
#
# Purpose: run speficied command with specified arguments and cache result. If cache is fresh enough, don't run command again but return cached output.
# Also cache exit status and stderr.
# License: GPLv3

# Use silly long variable names to avoid clashing with whatever the invoked program might use
RUNCACHED_MAX_AGE=${RUNCACHED_MAX_AGE:-300}
RUNCACHED_IGNORE_ENV=${RUNCACHED_IGNORE_ENV:-0}
RUNCACHED_IGNORE_PWD=${RUNCACHED_IGNORE_PWD:-0}
[[ -n "$HOME" ]] && RUNCACHED_CACHE_DIR=${RUNCACHED_CACHE_DIR:-$HOME/.runcached}
RUNCACHED_CACHE_DIR=${RUNCACHED_CACHE_DIR:-/var/cache/runcached}

function usage() {
    echo "Usage: runcached [--ttl <max cache age>] [--cache-dir <cache directory>]"
    echo "       [--ignore-env] [--ignore-pwd] [--help] [--prune-cache]"
    echo "       [--] command [arg1 [arg2...]]"
    echo
    echo "Run 'command' with the specified args and cache stdout, stderr and exit"
    echo "status. If you run the same command again and the cache is fresh, cached"
    echo "data is returned and the command is not actually run."
    echo
    echo "Normally, all exported environment variables as well as the current working"
    echo "directory are included in the cache key. The --ignore options disable this."
    echo "The OLDPWD variable is always ignored."
    echo
    echo "--prune-cache deletes all cache entries older than the maximum age. There is"
    echo "no other mechanism to prevent the cache growing without bounds."
    echo
    echo "The default cache directory is ${RUNCACHED_CACHE_DIR}."
    echo "Maximum cache age defaults to ${RUNCACHED_MAX_AGE}."
    echo
    echo "CAVEATS:"
    echo
    echo "Side effects of 'command' are obviously not cached."
    echo
    echo "There is no cache invalidation logic except cache age (specified in seconds)."
    echo
    echo "If the cache can't be created, the command is run uncached."
    echo
    echo "This script is always silent; any output comes from the invoked command. You"
    echo "may thus not notice errors creating the cache and such."
    echo
    echo "stdout and stderr streams are saved separately. When both are written to a"
    echo "terminal from cache, they will almost certainly be interleaved differently"
    echo "than originally. Ordering of messages within the two streams is preserved."
    exit 0
}

while [[ -n "$1" ]]; do
    case "$1" in
        --ttl)      RUNCACHED_MAX_AGE="$2"; shift 2;;
        --cache-dir)    RUNCACHED_CACHE_DIR="$2"; shift 2;;
        --ignore-env)   RUNCACHED_IGNORE_ENV=1; shift;;
        --ignore-pwd)   RUNCACHED_IGNORE_PWD=1; shift;;
        --prune-cache)  RUNCACHED_PRUNE=1; shift;;
        --help)     usage;;
        --)     shift; break;;
        *)      break;;
    esac
done

zmodload zsh/datetime
zmodload zsh/stat

# This is racy, but the race is harmless; at worst, the program is run uncached 
# because the cache is unusable. Testing for directory existence saves an
# mkdir(1) execution in the common case, improving performance infinitesimally;
# it could matter if runcached is run from inside a tight loop.
# Hide errors so that runcached itself is transparent (doesn't mix new messages 
# into whatever the called program outputs).
[[ -d "$RUNCACHED_CACHE_DIR/." ]] || mkdir -p "$RUNCACHED_CACHE_DIR" >/dev/null 2>/dev/null

((RUNCACHED_PRUNE)) && find "$RUNCACHED_CACHE_DIR/." -maxdepth 1 -type f \! -newermt @$[EPOCHSECONDS-RUNCACHED_MAX_AGE] -delete 2>/dev/null

[[ -n "$@" ]] || exit 0 # if no command specified, exit silently

(
    # Almost(?) nothing uses OLDPWD, but taking it into account potentially reduces cache efficency.
    # Thus, we ignore it for the purpose of coming up with a cache key.
    unset OLDPWD
    ((RUNCACHED_IGNORE_PWD)) && unset PWD
    ((RUNCACHED_IGNORE_ENV)) || env
    echo -E "$@"
) | md5sum | read RUNCACHED_CACHE_KEY RUNCACHED__crap__

# Unfortunately, I couldn't find a less convoluted way of getting rid of an error message when trying to open a logfile in a nonexistent cache directory.
exec {RUNCACHED_temp_stderr} >&2
exec 2>/dev/null
exec {RUNCACHED_LOCK_FD}>>$RUNCACHED_CACHE_DIR/$RUNCACHED_CACHE_KEY.lock
exec 2>&$RUNCACHED_temp_stderr
exec {RUNCACHED_temp_stderr}>&-

# If we can't obtain a lock, we want to run uncached; otherwise
# 'runcached' wouldn't be transparent because it would prevent
# parallel execution of several instances of the same command.
# Locking is necessary to avoid races between the mv(1) command
# below replacing stderr with a newer version and another instance
# of runcached using a newer stdout with the older stderr.
if flock -n $RUNCACHED_LOCK_FD 2>/dev/null; then
    if [[ -f $RUNCACHED_CACHE_DIR/$RUNCACHED_CACHE_KEY.stdout ]]; then
        if [[ $[EPOCHSECONDS-$(zstat +mtime $RUNCACHED_CACHE_DIR/$RUNCACHED_CACHE_KEY.stdout)] -le $RUNCACHED_MAX_AGE ]]; then
            cat $RUNCACHED_CACHE_DIR/$RUNCACHED_CACHE_KEY.stdout &
            cat $RUNCACHED_CACHE_DIR/$RUNCACHED_CACHE_KEY.stderr >&2 &
            wait
            exit $(<$RUNCACHED_CACHE_DIR/$RUNCACHED_CACHE_KEY.exitstatus)
        else
            rm -f $RUNCACHED_CACHE_DIR/$RUNCACHED_CACHE_KEY.{stdout,stderr,exitstatus} 2>/dev/null
        fi
    fi

    # only reached if cache didn't exist or was too old
    if [[ -d $RUNCACHED_CACHE_DIR/. ]]; then
        RUNCACHED_tempdir=$(mktemp -d 2>/dev/null)
        if [[ -d $RUNCACHED_tempdir/. ]]; then
            $@ >&1 >$RUNCACHED_tempdir/$RUNCACHED_CACHE_KEY.stdout 2>&2 2>$RUNCACHED_tempdir/$RUNCACHED_CACHE_KEY.stderr
            RUNCACHED_ret=$?
            echo $RUNCACHED_ret >$RUNCACHED_tempdir/$RUNCACHED_CACHE_KEY.exitstatus 2>/dev/null
            mv $RUNCACHED_tempdir/$RUNCACHED_CACHE_KEY.{stdout,stderr,exitstatus} $RUNCACHED_CACHE_DIR/ 2>/dev/null
            rmdir $RUNCACHED_tempdir 2>/dev/null
            exit $RUNCACHED_ret
        fi
    fi
fi

# only reached if cache not created successfully or lock couldn't be obtained
exec $@
2
27.01.2020, 21:30

Мир мэйнфреймов вызывает{ed,s} эту контрольную точку/перезапуск . Не из-за проблем с производительностью, а скорее для того, чтобы избежать потери компьютерного времени, вложенного в длительные -выполняемые задания. Эти утилиты обычно также фиксируют позиции файлов и т. д. для перезапуска заданий.

Поиск по запросу перезапуск контрольной точки linux возвращает около 400+ тыс. совпадений. Один из интересныхhttps://en.wikipedia.org/wiki/CRIUи компаньонhttps://criu.org/Comparison_to_other_CR_projects

С наилучшими пожеланиями... ура, дрл

0
27.01.2020, 21:30

Автор 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). См. README для получения дополнительной информации.

Работа над ним еще не завершена, но она функциональна и эффективна! Я уже использую его для ускорения командной оболочки и ряда других распространенных задач.

0
13.10.2021, 17:42

Теги

Похожие вопросы