Is there a shell command or utility for caching process output?












1














Hope this is the right place to ask.



Is there a unix tool that works similarly to this?



# invokes echo, saves to cache (using command with arguments as key), returns "hi"
cache 10 echo hi

# wait 2 seconds
sleep 2

# doesn't invoke echo, from cache returns "hi"
cache 10 echo hi

# wait 10 seconds
sleep 10

# with cache expired, invokes echo, returns "hi"
cache 10 echo hi


Obviously echo wouldn't be the real use-case.



Basically cache STDOUT, STDERR and status for a given command+arguments, so the next call to the same process doesn't have to re-run it.



I could write a script that does it but I wondered if there was one in the unix toolset that I don't know about.










share|improve this question




















  • 1




    Not that I know of, and I have a hard time figuring out a real use-case for this. Do you have something specific in mind?
    – Mat
    Jan 3 '17 at 14:12










  • Usually if people are doing something that requires performance to be considered they'll use something other than shell scripting. Shell scripting is mainly for simple stuff where you just need to run particular commands in a way that's too involved to manually type it out each time. You need a real scripting language.
    – Bratchley
    Jan 3 '17 at 16:33










  • Looking online there are examples of people using memcached via netcat you could also use /dev/shm and flat files but again that's more of a novelty thing than something you should really be doing.
    – Bratchley
    Jan 3 '17 at 16:36










  • There's not much call for such a tool because there aren't many commands whose output depends solely on their arguments (as opposed to file contents, data received from the network, user input, etc.). For things that depend only on file contents, there's make.
    – Gilles
    Jan 3 '17 at 23:28


















1














Hope this is the right place to ask.



Is there a unix tool that works similarly to this?



# invokes echo, saves to cache (using command with arguments as key), returns "hi"
cache 10 echo hi

# wait 2 seconds
sleep 2

# doesn't invoke echo, from cache returns "hi"
cache 10 echo hi

# wait 10 seconds
sleep 10

# with cache expired, invokes echo, returns "hi"
cache 10 echo hi


Obviously echo wouldn't be the real use-case.



Basically cache STDOUT, STDERR and status for a given command+arguments, so the next call to the same process doesn't have to re-run it.



I could write a script that does it but I wondered if there was one in the unix toolset that I don't know about.










share|improve this question




















  • 1




    Not that I know of, and I have a hard time figuring out a real use-case for this. Do you have something specific in mind?
    – Mat
    Jan 3 '17 at 14:12










  • Usually if people are doing something that requires performance to be considered they'll use something other than shell scripting. Shell scripting is mainly for simple stuff where you just need to run particular commands in a way that's too involved to manually type it out each time. You need a real scripting language.
    – Bratchley
    Jan 3 '17 at 16:33










  • Looking online there are examples of people using memcached via netcat you could also use /dev/shm and flat files but again that's more of a novelty thing than something you should really be doing.
    – Bratchley
    Jan 3 '17 at 16:36










  • There's not much call for such a tool because there aren't many commands whose output depends solely on their arguments (as opposed to file contents, data received from the network, user input, etc.). For things that depend only on file contents, there's make.
    – Gilles
    Jan 3 '17 at 23:28
















1












1








1


1





Hope this is the right place to ask.



Is there a unix tool that works similarly to this?



# invokes echo, saves to cache (using command with arguments as key), returns "hi"
cache 10 echo hi

# wait 2 seconds
sleep 2

# doesn't invoke echo, from cache returns "hi"
cache 10 echo hi

# wait 10 seconds
sleep 10

# with cache expired, invokes echo, returns "hi"
cache 10 echo hi


Obviously echo wouldn't be the real use-case.



Basically cache STDOUT, STDERR and status for a given command+arguments, so the next call to the same process doesn't have to re-run it.



I could write a script that does it but I wondered if there was one in the unix toolset that I don't know about.










share|improve this question















Hope this is the right place to ask.



Is there a unix tool that works similarly to this?



# invokes echo, saves to cache (using command with arguments as key), returns "hi"
cache 10 echo hi

# wait 2 seconds
sleep 2

# doesn't invoke echo, from cache returns "hi"
cache 10 echo hi

# wait 10 seconds
sleep 10

# with cache expired, invokes echo, returns "hi"
cache 10 echo hi


Obviously echo wouldn't be the real use-case.



Basically cache STDOUT, STDERR and status for a given command+arguments, so the next call to the same process doesn't have to re-run it.



I could write a script that does it but I wondered if there was one in the unix toolset that I don't know about.







shell-script shell utilities






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 3 '17 at 14:00









mattdm

28.1k1172112




28.1k1172112










asked Jan 3 '17 at 13:53









Dan2552

1062




1062








  • 1




    Not that I know of, and I have a hard time figuring out a real use-case for this. Do you have something specific in mind?
    – Mat
    Jan 3 '17 at 14:12










  • Usually if people are doing something that requires performance to be considered they'll use something other than shell scripting. Shell scripting is mainly for simple stuff where you just need to run particular commands in a way that's too involved to manually type it out each time. You need a real scripting language.
    – Bratchley
    Jan 3 '17 at 16:33










  • Looking online there are examples of people using memcached via netcat you could also use /dev/shm and flat files but again that's more of a novelty thing than something you should really be doing.
    – Bratchley
    Jan 3 '17 at 16:36










  • There's not much call for such a tool because there aren't many commands whose output depends solely on their arguments (as opposed to file contents, data received from the network, user input, etc.). For things that depend only on file contents, there's make.
    – Gilles
    Jan 3 '17 at 23:28
















  • 1




    Not that I know of, and I have a hard time figuring out a real use-case for this. Do you have something specific in mind?
    – Mat
    Jan 3 '17 at 14:12










  • Usually if people are doing something that requires performance to be considered they'll use something other than shell scripting. Shell scripting is mainly for simple stuff where you just need to run particular commands in a way that's too involved to manually type it out each time. You need a real scripting language.
    – Bratchley
    Jan 3 '17 at 16:33










  • Looking online there are examples of people using memcached via netcat you could also use /dev/shm and flat files but again that's more of a novelty thing than something you should really be doing.
    – Bratchley
    Jan 3 '17 at 16:36










  • There's not much call for such a tool because there aren't many commands whose output depends solely on their arguments (as opposed to file contents, data received from the network, user input, etc.). For things that depend only on file contents, there's make.
    – Gilles
    Jan 3 '17 at 23:28










1




1




Not that I know of, and I have a hard time figuring out a real use-case for this. Do you have something specific in mind?
– Mat
Jan 3 '17 at 14:12




Not that I know of, and I have a hard time figuring out a real use-case for this. Do you have something specific in mind?
– Mat
Jan 3 '17 at 14:12












Usually if people are doing something that requires performance to be considered they'll use something other than shell scripting. Shell scripting is mainly for simple stuff where you just need to run particular commands in a way that's too involved to manually type it out each time. You need a real scripting language.
– Bratchley
Jan 3 '17 at 16:33




Usually if people are doing something that requires performance to be considered they'll use something other than shell scripting. Shell scripting is mainly for simple stuff where you just need to run particular commands in a way that's too involved to manually type it out each time. You need a real scripting language.
– Bratchley
Jan 3 '17 at 16:33












Looking online there are examples of people using memcached via netcat you could also use /dev/shm and flat files but again that's more of a novelty thing than something you should really be doing.
– Bratchley
Jan 3 '17 at 16:36




Looking online there are examples of people using memcached via netcat you could also use /dev/shm and flat files but again that's more of a novelty thing than something you should really be doing.
– Bratchley
Jan 3 '17 at 16:36












There's not much call for such a tool because there aren't many commands whose output depends solely on their arguments (as opposed to file contents, data received from the network, user input, etc.). For things that depend only on file contents, there's make.
– Gilles
Jan 3 '17 at 23:28






There's not much call for such a tool because there aren't many commands whose output depends solely on their arguments (as opposed to file contents, data received from the network, user input, etc.). For things that depend only on file contents, there's make.
– Gilles
Jan 3 '17 at 23:28












5 Answers
5






active

oldest

votes


















1














You could put the result in a file and then read it back from that file...



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) )


From this we could define a "cache" function. This version need a character that we will never use as argument. For example the comma ",". You can change it at the line "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
}


The timeout could be implemented with "date +%s" and "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
}


The "bash only" line could be replaced by :



  "$@" 2> "$tmpDir/$cmd"_stderr > "$tmpDir/$cmd"_stdout
local e=$?
cat "$tmpDir/$cmd"_stdout
cat "$tmpDir/$cmd"_stderr 1>&2





share|improve this answer























  • /tmp isn't necessarily going to be tmpfs and so the pages could be evicted from the filesystem cache or create an undesirable dependency (booting from network, etc). If you're going to use flat files it's probably better to use /dev/shm or something that's always stored in RAM.
    – Bratchley
    Jan 3 '17 at 16:41










  • Also, minor quibbles with your specific implementation: 1) $$ by itself probably isn't unique enough since some PID's can be pretty small and you're rm -rf-ing in a global directory after all. Probably better to have a static prefix for this script like tmpDir=/dev/shm/bashCache.$$ or something
    – Bratchley
    Jan 3 '17 at 16:48








  • 1




    2) You really should have eviction happen parallel to script execution and not on each save/retrieval of individual items otherwise your cache could become huge and unlike the filesystem cache the memory could just permanently stay used even if there's another process that needs it and the cache entry has expired
    – Bratchley
    Jan 3 '17 at 16:48










  • @Bratchley on macOS I get ls: /dev/shm: No such file or directory
    – Dan2552
    Jan 3 '17 at 18:37










  • @Dan2552 then use a ramdisk if you're on OS X. Point being that you need to ensure the pages don't get swapped out to disk and stay in memory. I can't edit the answer to make it work since that'd be a substantive change and kind of Vouze's call on whether he wants to include it.
    – Bratchley
    Jan 3 '17 at 19:33



















0














To my knowledge, there is no standard or traditional tool which works this way. However, searching for "cache stdout" brought me to https://github.com/Valiev/cache, which appears to be a tool which does what you want.






share|improve this answer





















  • This requires to install Python !! Everything could be done in shell, without any additional tool.
    – Vouze
    Jan 3 '17 at 16:14












  • Sure. But if you have Python installed, there you go.
    – mattdm
    Jan 3 '17 at 16:18



















0














I'm not aware of a generic tool for doing this sort of caching, but I'm aware of a specific tool for caching the result of compiling C or C++ files, the ccache compiler cache.



This stores the preprocessed sources and compiled object code files, and uses hashes based on both the source code and the compiler signature to figure out whether to use the cache or to recompile a file.



A generic cache for the shell would have to similarly hash the script, the signature of each external utility used, etc., and also take into account that the output of a shell script may depend on the time of day (if it uses date) and many many other factors.



Creating a cache for a single command would not be beneficial, especially not for echo since this is built into most shells and the extra processing of it for caching would only slow it down (unless what was echoed also called heavy external utilities through command substitutions etc.)



I don't think it would be worthwhile in the end, but possibly only useful in a very specific context, such as the ccache utility for caching compilations, which is a problem slightly more limited in its scope.






share|improve this answer























  • ccache only works with gcc and only knows how to cache the compilation of a single C/C++/Objective-C/Objective-C++ file
    – Vouze
    Jan 3 '17 at 16:17



















0














The most common mechanism for caching command output in shell scripts is to assign it to a variable. This is easily done with a subshell. It doesn't expire in the way a traditional cache does, but those who write shell scripts most often find it acceptable. Here's your above script using a subshell and variable



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


Another alternative is to create a cache function in shell script. Something like ...



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
}





share|improve this answer





















  • If the variable is not an environment variable (not exported), then it should be lower-case. This is a useful tradition, to avoid bugs.
    – ctrl-alt-delor
    Dec 11 at 7:52



















-1














use this cmd https://github.com/jingminglang/cmd_cache



git clone https://github.com/jingminglang/cmd_cache



go build main.go



./main -c [your command] -t [cache time]






share|improve this answer





















    Your Answer








    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "106"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f334540%2fis-there-a-shell-command-or-utility-for-caching-process-output%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    5 Answers
    5






    active

    oldest

    votes








    5 Answers
    5






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1














    You could put the result in a file and then read it back from that file...



    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) )


    From this we could define a "cache" function. This version need a character that we will never use as argument. For example the comma ",". You can change it at the line "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
    }


    The timeout could be implemented with "date +%s" and "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
    }


    The "bash only" line could be replaced by :



      "$@" 2> "$tmpDir/$cmd"_stderr > "$tmpDir/$cmd"_stdout
    local e=$?
    cat "$tmpDir/$cmd"_stdout
    cat "$tmpDir/$cmd"_stderr 1>&2





    share|improve this answer























    • /tmp isn't necessarily going to be tmpfs and so the pages could be evicted from the filesystem cache or create an undesirable dependency (booting from network, etc). If you're going to use flat files it's probably better to use /dev/shm or something that's always stored in RAM.
      – Bratchley
      Jan 3 '17 at 16:41










    • Also, minor quibbles with your specific implementation: 1) $$ by itself probably isn't unique enough since some PID's can be pretty small and you're rm -rf-ing in a global directory after all. Probably better to have a static prefix for this script like tmpDir=/dev/shm/bashCache.$$ or something
      – Bratchley
      Jan 3 '17 at 16:48








    • 1




      2) You really should have eviction happen parallel to script execution and not on each save/retrieval of individual items otherwise your cache could become huge and unlike the filesystem cache the memory could just permanently stay used even if there's another process that needs it and the cache entry has expired
      – Bratchley
      Jan 3 '17 at 16:48










    • @Bratchley on macOS I get ls: /dev/shm: No such file or directory
      – Dan2552
      Jan 3 '17 at 18:37










    • @Dan2552 then use a ramdisk if you're on OS X. Point being that you need to ensure the pages don't get swapped out to disk and stay in memory. I can't edit the answer to make it work since that'd be a substantive change and kind of Vouze's call on whether he wants to include it.
      – Bratchley
      Jan 3 '17 at 19:33
















    1














    You could put the result in a file and then read it back from that file...



    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) )


    From this we could define a "cache" function. This version need a character that we will never use as argument. For example the comma ",". You can change it at the line "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
    }


    The timeout could be implemented with "date +%s" and "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
    }


    The "bash only" line could be replaced by :



      "$@" 2> "$tmpDir/$cmd"_stderr > "$tmpDir/$cmd"_stdout
    local e=$?
    cat "$tmpDir/$cmd"_stdout
    cat "$tmpDir/$cmd"_stderr 1>&2





    share|improve this answer























    • /tmp isn't necessarily going to be tmpfs and so the pages could be evicted from the filesystem cache or create an undesirable dependency (booting from network, etc). If you're going to use flat files it's probably better to use /dev/shm or something that's always stored in RAM.
      – Bratchley
      Jan 3 '17 at 16:41










    • Also, minor quibbles with your specific implementation: 1) $$ by itself probably isn't unique enough since some PID's can be pretty small and you're rm -rf-ing in a global directory after all. Probably better to have a static prefix for this script like tmpDir=/dev/shm/bashCache.$$ or something
      – Bratchley
      Jan 3 '17 at 16:48








    • 1




      2) You really should have eviction happen parallel to script execution and not on each save/retrieval of individual items otherwise your cache could become huge and unlike the filesystem cache the memory could just permanently stay used even if there's another process that needs it and the cache entry has expired
      – Bratchley
      Jan 3 '17 at 16:48










    • @Bratchley on macOS I get ls: /dev/shm: No such file or directory
      – Dan2552
      Jan 3 '17 at 18:37










    • @Dan2552 then use a ramdisk if you're on OS X. Point being that you need to ensure the pages don't get swapped out to disk and stay in memory. I can't edit the answer to make it work since that'd be a substantive change and kind of Vouze's call on whether he wants to include it.
      – Bratchley
      Jan 3 '17 at 19:33














    1












    1








    1






    You could put the result in a file and then read it back from that file...



    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) )


    From this we could define a "cache" function. This version need a character that we will never use as argument. For example the comma ",". You can change it at the line "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
    }


    The timeout could be implemented with "date +%s" and "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
    }


    The "bash only" line could be replaced by :



      "$@" 2> "$tmpDir/$cmd"_stderr > "$tmpDir/$cmd"_stdout
    local e=$?
    cat "$tmpDir/$cmd"_stdout
    cat "$tmpDir/$cmd"_stderr 1>&2





    share|improve this answer














    You could put the result in a file and then read it back from that file...



    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) )


    From this we could define a "cache" function. This version need a character that we will never use as argument. For example the comma ",". You can change it at the line "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
    }


    The timeout could be implemented with "date +%s" and "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
    }


    The "bash only" line could be replaced by :



      "$@" 2> "$tmpDir/$cmd"_stderr > "$tmpDir/$cmd"_stdout
    local e=$?
    cat "$tmpDir/$cmd"_stdout
    cat "$tmpDir/$cmd"_stderr 1>&2






    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Jan 3 '17 at 17:08









    Bratchley

    11.9k74388




    11.9k74388










    answered Jan 3 '17 at 15:51









    Vouze

    62037




    62037












    • /tmp isn't necessarily going to be tmpfs and so the pages could be evicted from the filesystem cache or create an undesirable dependency (booting from network, etc). If you're going to use flat files it's probably better to use /dev/shm or something that's always stored in RAM.
      – Bratchley
      Jan 3 '17 at 16:41










    • Also, minor quibbles with your specific implementation: 1) $$ by itself probably isn't unique enough since some PID's can be pretty small and you're rm -rf-ing in a global directory after all. Probably better to have a static prefix for this script like tmpDir=/dev/shm/bashCache.$$ or something
      – Bratchley
      Jan 3 '17 at 16:48








    • 1




      2) You really should have eviction happen parallel to script execution and not on each save/retrieval of individual items otherwise your cache could become huge and unlike the filesystem cache the memory could just permanently stay used even if there's another process that needs it and the cache entry has expired
      – Bratchley
      Jan 3 '17 at 16:48










    • @Bratchley on macOS I get ls: /dev/shm: No such file or directory
      – Dan2552
      Jan 3 '17 at 18:37










    • @Dan2552 then use a ramdisk if you're on OS X. Point being that you need to ensure the pages don't get swapped out to disk and stay in memory. I can't edit the answer to make it work since that'd be a substantive change and kind of Vouze's call on whether he wants to include it.
      – Bratchley
      Jan 3 '17 at 19:33


















    • /tmp isn't necessarily going to be tmpfs and so the pages could be evicted from the filesystem cache or create an undesirable dependency (booting from network, etc). If you're going to use flat files it's probably better to use /dev/shm or something that's always stored in RAM.
      – Bratchley
      Jan 3 '17 at 16:41










    • Also, minor quibbles with your specific implementation: 1) $$ by itself probably isn't unique enough since some PID's can be pretty small and you're rm -rf-ing in a global directory after all. Probably better to have a static prefix for this script like tmpDir=/dev/shm/bashCache.$$ or something
      – Bratchley
      Jan 3 '17 at 16:48








    • 1




      2) You really should have eviction happen parallel to script execution and not on each save/retrieval of individual items otherwise your cache could become huge and unlike the filesystem cache the memory could just permanently stay used even if there's another process that needs it and the cache entry has expired
      – Bratchley
      Jan 3 '17 at 16:48










    • @Bratchley on macOS I get ls: /dev/shm: No such file or directory
      – Dan2552
      Jan 3 '17 at 18:37










    • @Dan2552 then use a ramdisk if you're on OS X. Point being that you need to ensure the pages don't get swapped out to disk and stay in memory. I can't edit the answer to make it work since that'd be a substantive change and kind of Vouze's call on whether he wants to include it.
      – Bratchley
      Jan 3 '17 at 19:33
















    /tmp isn't necessarily going to be tmpfs and so the pages could be evicted from the filesystem cache or create an undesirable dependency (booting from network, etc). If you're going to use flat files it's probably better to use /dev/shm or something that's always stored in RAM.
    – Bratchley
    Jan 3 '17 at 16:41




    /tmp isn't necessarily going to be tmpfs and so the pages could be evicted from the filesystem cache or create an undesirable dependency (booting from network, etc). If you're going to use flat files it's probably better to use /dev/shm or something that's always stored in RAM.
    – Bratchley
    Jan 3 '17 at 16:41












    Also, minor quibbles with your specific implementation: 1) $$ by itself probably isn't unique enough since some PID's can be pretty small and you're rm -rf-ing in a global directory after all. Probably better to have a static prefix for this script like tmpDir=/dev/shm/bashCache.$$ or something
    – Bratchley
    Jan 3 '17 at 16:48






    Also, minor quibbles with your specific implementation: 1) $$ by itself probably isn't unique enough since some PID's can be pretty small and you're rm -rf-ing in a global directory after all. Probably better to have a static prefix for this script like tmpDir=/dev/shm/bashCache.$$ or something
    – Bratchley
    Jan 3 '17 at 16:48






    1




    1




    2) You really should have eviction happen parallel to script execution and not on each save/retrieval of individual items otherwise your cache could become huge and unlike the filesystem cache the memory could just permanently stay used even if there's another process that needs it and the cache entry has expired
    – Bratchley
    Jan 3 '17 at 16:48




    2) You really should have eviction happen parallel to script execution and not on each save/retrieval of individual items otherwise your cache could become huge and unlike the filesystem cache the memory could just permanently stay used even if there's another process that needs it and the cache entry has expired
    – Bratchley
    Jan 3 '17 at 16:48












    @Bratchley on macOS I get ls: /dev/shm: No such file or directory
    – Dan2552
    Jan 3 '17 at 18:37




    @Bratchley on macOS I get ls: /dev/shm: No such file or directory
    – Dan2552
    Jan 3 '17 at 18:37












    @Dan2552 then use a ramdisk if you're on OS X. Point being that you need to ensure the pages don't get swapped out to disk and stay in memory. I can't edit the answer to make it work since that'd be a substantive change and kind of Vouze's call on whether he wants to include it.
    – Bratchley
    Jan 3 '17 at 19:33




    @Dan2552 then use a ramdisk if you're on OS X. Point being that you need to ensure the pages don't get swapped out to disk and stay in memory. I can't edit the answer to make it work since that'd be a substantive change and kind of Vouze's call on whether he wants to include it.
    – Bratchley
    Jan 3 '17 at 19:33













    0














    To my knowledge, there is no standard or traditional tool which works this way. However, searching for "cache stdout" brought me to https://github.com/Valiev/cache, which appears to be a tool which does what you want.






    share|improve this answer





















    • This requires to install Python !! Everything could be done in shell, without any additional tool.
      – Vouze
      Jan 3 '17 at 16:14












    • Sure. But if you have Python installed, there you go.
      – mattdm
      Jan 3 '17 at 16:18
















    0














    To my knowledge, there is no standard or traditional tool which works this way. However, searching for "cache stdout" brought me to https://github.com/Valiev/cache, which appears to be a tool which does what you want.






    share|improve this answer





















    • This requires to install Python !! Everything could be done in shell, without any additional tool.
      – Vouze
      Jan 3 '17 at 16:14












    • Sure. But if you have Python installed, there you go.
      – mattdm
      Jan 3 '17 at 16:18














    0












    0








    0






    To my knowledge, there is no standard or traditional tool which works this way. However, searching for "cache stdout" brought me to https://github.com/Valiev/cache, which appears to be a tool which does what you want.






    share|improve this answer












    To my knowledge, there is no standard or traditional tool which works this way. However, searching for "cache stdout" brought me to https://github.com/Valiev/cache, which appears to be a tool which does what you want.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Jan 3 '17 at 14:02









    mattdm

    28.1k1172112




    28.1k1172112












    • This requires to install Python !! Everything could be done in shell, without any additional tool.
      – Vouze
      Jan 3 '17 at 16:14












    • Sure. But if you have Python installed, there you go.
      – mattdm
      Jan 3 '17 at 16:18


















    • This requires to install Python !! Everything could be done in shell, without any additional tool.
      – Vouze
      Jan 3 '17 at 16:14












    • Sure. But if you have Python installed, there you go.
      – mattdm
      Jan 3 '17 at 16:18
















    This requires to install Python !! Everything could be done in shell, without any additional tool.
    – Vouze
    Jan 3 '17 at 16:14






    This requires to install Python !! Everything could be done in shell, without any additional tool.
    – Vouze
    Jan 3 '17 at 16:14














    Sure. But if you have Python installed, there you go.
    – mattdm
    Jan 3 '17 at 16:18




    Sure. But if you have Python installed, there you go.
    – mattdm
    Jan 3 '17 at 16:18











    0














    I'm not aware of a generic tool for doing this sort of caching, but I'm aware of a specific tool for caching the result of compiling C or C++ files, the ccache compiler cache.



    This stores the preprocessed sources and compiled object code files, and uses hashes based on both the source code and the compiler signature to figure out whether to use the cache or to recompile a file.



    A generic cache for the shell would have to similarly hash the script, the signature of each external utility used, etc., and also take into account that the output of a shell script may depend on the time of day (if it uses date) and many many other factors.



    Creating a cache for a single command would not be beneficial, especially not for echo since this is built into most shells and the extra processing of it for caching would only slow it down (unless what was echoed also called heavy external utilities through command substitutions etc.)



    I don't think it would be worthwhile in the end, but possibly only useful in a very specific context, such as the ccache utility for caching compilations, which is a problem slightly more limited in its scope.






    share|improve this answer























    • ccache only works with gcc and only knows how to cache the compilation of a single C/C++/Objective-C/Objective-C++ file
      – Vouze
      Jan 3 '17 at 16:17
















    0














    I'm not aware of a generic tool for doing this sort of caching, but I'm aware of a specific tool for caching the result of compiling C or C++ files, the ccache compiler cache.



    This stores the preprocessed sources and compiled object code files, and uses hashes based on both the source code and the compiler signature to figure out whether to use the cache or to recompile a file.



    A generic cache for the shell would have to similarly hash the script, the signature of each external utility used, etc., and also take into account that the output of a shell script may depend on the time of day (if it uses date) and many many other factors.



    Creating a cache for a single command would not be beneficial, especially not for echo since this is built into most shells and the extra processing of it for caching would only slow it down (unless what was echoed also called heavy external utilities through command substitutions etc.)



    I don't think it would be worthwhile in the end, but possibly only useful in a very specific context, such as the ccache utility for caching compilations, which is a problem slightly more limited in its scope.






    share|improve this answer























    • ccache only works with gcc and only knows how to cache the compilation of a single C/C++/Objective-C/Objective-C++ file
      – Vouze
      Jan 3 '17 at 16:17














    0












    0








    0






    I'm not aware of a generic tool for doing this sort of caching, but I'm aware of a specific tool for caching the result of compiling C or C++ files, the ccache compiler cache.



    This stores the preprocessed sources and compiled object code files, and uses hashes based on both the source code and the compiler signature to figure out whether to use the cache or to recompile a file.



    A generic cache for the shell would have to similarly hash the script, the signature of each external utility used, etc., and also take into account that the output of a shell script may depend on the time of day (if it uses date) and many many other factors.



    Creating a cache for a single command would not be beneficial, especially not for echo since this is built into most shells and the extra processing of it for caching would only slow it down (unless what was echoed also called heavy external utilities through command substitutions etc.)



    I don't think it would be worthwhile in the end, but possibly only useful in a very specific context, such as the ccache utility for caching compilations, which is a problem slightly more limited in its scope.






    share|improve this answer














    I'm not aware of a generic tool for doing this sort of caching, but I'm aware of a specific tool for caching the result of compiling C or C++ files, the ccache compiler cache.



    This stores the preprocessed sources and compiled object code files, and uses hashes based on both the source code and the compiler signature to figure out whether to use the cache or to recompile a file.



    A generic cache for the shell would have to similarly hash the script, the signature of each external utility used, etc., and also take into account that the output of a shell script may depend on the time of day (if it uses date) and many many other factors.



    Creating a cache for a single command would not be beneficial, especially not for echo since this is built into most shells and the extra processing of it for caching would only slow it down (unless what was echoed also called heavy external utilities through command substitutions etc.)



    I don't think it would be worthwhile in the end, but possibly only useful in a very specific context, such as the ccache utility for caching compilations, which is a problem slightly more limited in its scope.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Jan 3 '17 at 14:19

























    answered Jan 3 '17 at 14:14









    Kusalananda

    121k16228372




    121k16228372












    • ccache only works with gcc and only knows how to cache the compilation of a single C/C++/Objective-C/Objective-C++ file
      – Vouze
      Jan 3 '17 at 16:17


















    • ccache only works with gcc and only knows how to cache the compilation of a single C/C++/Objective-C/Objective-C++ file
      – Vouze
      Jan 3 '17 at 16:17
















    ccache only works with gcc and only knows how to cache the compilation of a single C/C++/Objective-C/Objective-C++ file
    – Vouze
    Jan 3 '17 at 16:17




    ccache only works with gcc and only knows how to cache the compilation of a single C/C++/Objective-C/Objective-C++ file
    – Vouze
    Jan 3 '17 at 16:17











    0














    The most common mechanism for caching command output in shell scripts is to assign it to a variable. This is easily done with a subshell. It doesn't expire in the way a traditional cache does, but those who write shell scripts most often find it acceptable. Here's your above script using a subshell and variable



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


    Another alternative is to create a cache function in shell script. Something like ...



    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
    }





    share|improve this answer





















    • If the variable is not an environment variable (not exported), then it should be lower-case. This is a useful tradition, to avoid bugs.
      – ctrl-alt-delor
      Dec 11 at 7:52
















    0














    The most common mechanism for caching command output in shell scripts is to assign it to a variable. This is easily done with a subshell. It doesn't expire in the way a traditional cache does, but those who write shell scripts most often find it acceptable. Here's your above script using a subshell and variable



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


    Another alternative is to create a cache function in shell script. Something like ...



    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
    }





    share|improve this answer





















    • If the variable is not an environment variable (not exported), then it should be lower-case. This is a useful tradition, to avoid bugs.
      – ctrl-alt-delor
      Dec 11 at 7:52














    0












    0








    0






    The most common mechanism for caching command output in shell scripts is to assign it to a variable. This is easily done with a subshell. It doesn't expire in the way a traditional cache does, but those who write shell scripts most often find it acceptable. Here's your above script using a subshell and variable



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


    Another alternative is to create a cache function in shell script. Something like ...



    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
    }





    share|improve this answer












    The most common mechanism for caching command output in shell scripts is to assign it to a variable. This is easily done with a subshell. It doesn't expire in the way a traditional cache does, but those who write shell scripts most often find it acceptable. Here's your above script using a subshell and variable



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


    Another alternative is to create a cache function in shell script. Something like ...



    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
    }






    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Jan 3 '17 at 15:36









    smokes2345

    717314




    717314












    • If the variable is not an environment variable (not exported), then it should be lower-case. This is a useful tradition, to avoid bugs.
      – ctrl-alt-delor
      Dec 11 at 7:52


















    • If the variable is not an environment variable (not exported), then it should be lower-case. This is a useful tradition, to avoid bugs.
      – ctrl-alt-delor
      Dec 11 at 7:52
















    If the variable is not an environment variable (not exported), then it should be lower-case. This is a useful tradition, to avoid bugs.
    – ctrl-alt-delor
    Dec 11 at 7:52




    If the variable is not an environment variable (not exported), then it should be lower-case. This is a useful tradition, to avoid bugs.
    – ctrl-alt-delor
    Dec 11 at 7:52











    -1














    use this cmd https://github.com/jingminglang/cmd_cache



    git clone https://github.com/jingminglang/cmd_cache



    go build main.go



    ./main -c [your command] -t [cache time]






    share|improve this answer


























      -1














      use this cmd https://github.com/jingminglang/cmd_cache



      git clone https://github.com/jingminglang/cmd_cache



      go build main.go



      ./main -c [your command] -t [cache time]






      share|improve this answer
























        -1












        -1








        -1






        use this cmd https://github.com/jingminglang/cmd_cache



        git clone https://github.com/jingminglang/cmd_cache



        go build main.go



        ./main -c [your command] -t [cache time]






        share|improve this answer












        use this cmd https://github.com/jingminglang/cmd_cache



        git clone https://github.com/jingminglang/cmd_cache



        go build main.go



        ./main -c [your command] -t [cache time]







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Dec 11 at 6:51









        Minglang Jing

        1




        1






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Unix & Linux Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f334540%2fis-there-a-shell-command-or-utility-for-caching-process-output%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            List directoties down one level, excluding some named directories and files

            list processes belonging to a network namespace

            list systemd RuntimeDirectory mounts