4

I have several servers which have several files with deploy date info I need to parse, and get a local copy of the files which are 2 months old or older.

#!/bin/bash

# split on new line not space
# don't want to quote everything
# globbing desired
IFS=$'\n'

servers=(
  blue
  red
)

parseDate() {
    grep 'deploy_date:' $file | \
    sed  ...                  | \
    # much more in here...
}

getOldFiles() {
    local #all vars
    currentDateInSecs=$(date +%s)
    twoMonthsInSecs=526000
    for server in ${servers[@]}; do
        oldfile=$(
          ssh $server "
            $(declare -f parseDate)
            for file in /dir/$server/file*; do
                dateInSecs=$(date -jf '%b/%e/%Y' $(parseDate) +%s)
                timeDiff=$((\$currentDateInSecs - \$dateInSecs))
                ((\$timeDiff >= \$twoMonthsInSecs)) &&
                    cat \$file
            done
          "
        )
        [ -n $oldFile ] &&
            cat $oldFile ~/oldFiles/${server}-${file}.txt
    done

Problem:

Current issue is with dateInSecs=$(date -jf '%b/%e/%Y' $(parseDate \$file) +%s).

When parseDate \$file is in $() the $file variable is not expanded, it works fine without command substitution, but I need it.

How do I fix this?

Info:

This isn't a script, they're in my ~/.bash_profile

This is a script (among other scripts) in a git repo which is sourced from ~/bash_profile (I have an install script which sets sources using $PWD) so people can use these commands directly instead of cd'ing to the git repo (which has many other things not applicable to the them).

Runs from Macos to CentOS servers.

12
  • 1
    made updates for clarity.
    – Nickotine
    Commented Jan 30, 2024 at 17:34
  • The -j argument doesn't exist for date command Commented Jan 30, 2024 at 17:47
  • 1
    @EdgarCivil it does on macOS and BSD. However, that's a valid point, the date on the CentOS machine won't have it, most likely. That will be the next problem. Nickotine, are you sure your date -jf '%b/%e/%Y' command works when not in the $()? Have you installed BSD date on the CentOS server?
    – terdon
    Commented Jan 30, 2024 at 17:57
  • 3
    TIL fn() { echo thing; }; ssh remoteHost "$(declare -f fn); fn". Wild! Commented Jan 30, 2024 at 18:02
  • 1
    Instead of all these issues you're having with quoting due to trying to call what's essentially a shell script via the command line of ssh, why don't you simply write the script for local execution and then transfer it to the remote machine. You may then execute it much easier.
    – Kusalananda
    Commented Jan 30, 2024 at 19:40

2 Answers 2

6

Everything that can be expanded inside your oldfile=$(...) command substitution is being expanded before you ssh. You need to escape the various internal $() if you want them to run on the remote instead of the local. Anything you want to be evaluated on the remote, needs escaping. Try:

oldfile=$(
          ssh $server "
            $(declare -f parseDate)
            for file in /dir/$server/file*; do
                dateInSecs=\$(date -jf '%b/%e/%Y' \$(parseDate \$file) +%s)
                timeDiff=\$(($currentDateInSecs - \$dateInSecs))
                ((\$timeDiff >= \$twoMonthsInSecs)) &&
                    cat \$file
            done
          "
        )

Also, while BSD date has the -j option, GNU date does not. Since your are connecting to a CentOS machine, you probably want to change

dateInSecs=\$(date -jf '%b/%e/%Y' \$(parseDate \$file) +%s)

to

dateInSecs=\$(date \$(parseDate \$file) +%s)

But we would need to see the exact format you get from parseDate to be sure.

6
  • 1
    and they need to not escape the ones they want to expand locally, like $server, but also$currentDateInSecs
    – ilkkachu
    Commented Jan 30, 2024 at 18:02
  • @ilkkachu ah yes, good catch, I missed that $currentDateInSecs is defined in the local.
    – terdon
    Commented Jan 30, 2024 at 18:04
  • I might've tried escaping all the $() but I'll retry
    – Nickotine
    Commented Jan 31, 2024 at 4:54
  • Think this will work just noticed what you did with the variable, thanks will try tomorrow
    – Nickotine
    Commented Jan 31, 2024 at 12:07
  • also had to use gnu date which was much easier to work with.
    – Nickotine
    Commented Feb 2, 2024 at 17:28
3

Be careful with your double and single quotes. Where you use double quotes, all $variables and $( … ) expressions will be evaluated before the command is executed. For example:

ssh $server "
    $(declare -f parseDate)
    for file in /dir/$server/file*; do
        dateInSecs=$(date -jf '%b/%e/%Y' $(parseDate \$file) +%s)
        timeDiff=$((\$currentDateInSecs - \$dateInSecs))
        ((\$timeDiff >= \$twoMonthsInSecs)) && cat \$file
    done
"

Here, if $server is remoteHost, this will be prepared as something like this before being passed through ssh to the remote $server:

ssh remoteHost "
    parseDate() { … }
    for file in /dir/remoteHost/file*; do
        dateInSecs=
        timeDiff=
        ((\$timeDiff >= \$twoMonthsInSecs)) && cat \$file
    done
"

And on the remoteHost itself, you'll try to execute something like this:

    parseDate() { … }
    for file in /dir/remoteHost/file*; do
        dateInSecs=
        timeDiff=
        (( >= )) && cat $file
    done

If you can amend your question to include an explanation of what you're trying to achieve, along with example input and output, someone here may be able to suggest how to rewrite your code to fit the requirements.

3
  • That should probably be (($timeDiff >= $twoMonthsInSecs)) && cat $file on the last line. But anyway, $((\$currentDateInSecs - \$dateInSecs)) already croaks with a syntax error on Bash, so the whole ssh command won't run.
    – ilkkachu
    Commented Jan 30, 2024 at 19:28
  • If I could avoid wrapping the ssh cmd in quotes it would make things so much easier... I can't use a heredoc as the stuff is in a function. Is there something else I can wrap the ssh cmd in?
    – Nickotine
    Commented Jan 31, 2024 at 12:00
  • @Nickotine if you amend your question to include an explanation of what you're trying to achieve, along with example input and output, someone here may be able to suggest how to modify your code Commented Jan 31, 2024 at 13:56

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.