Search code examples
linuxbashunixssh

find timestamp in remote server (ssh) specific pattern filename


I need to create a script that should get the newest file based on a filename pattern from server A on server B.

The command I run on server B is like this (server A is 103.11.34.111):

ssh user@103.11.34.111 'find /data/path -name "CDR*$file_date" -printf "%TY-%Tm-%Td %TH:%TM:%.2TS\n" | sort -rn | head -1'

$file_date is a variable that defines a date with format YYYYMMDD

I get error as below when I run ./myscript.sh

*** buffer overflow detected ***: ssh terminated

I suspect there is issue in my command because if I run the command with

ssh user@103.11.34.111 'find /data/path -name "CDR*$file_date" | sort -rn | head -1'

or

ssh user@103.11.34.111 'find /data/path -printf "%TY-%Tm-%Td %TH:%TM:%.2TS\n" | sort -rn | head -1'

it manages to get the result, however if I run with full parameters there is the buffer overflow.


Solution

  • The value of $file_date is not visible on the remote server because you are using single quotes. The crucial change to fix that is to use double quotes. You have to switch to single quotes internally (or otherwise escape or quote the other quotes).

    ssh user@103.11.34.111 "find /data/path -name 'CDR*$file_date' -printf '%TY-%Tm-%Td %TH:%TM:%.2TS\n' | sort -rn | head -1"
    

    See also Difference between single and double quotes in Bash

    The pipe to sort | head is rather memory-intensive; keeping all the lines in memory so that they can be sorted is superfluous when all you need is the overall maximum value. So the following should be both more efficient in terms of memory and run faster.

    ssh user@103.11.34.111 "find /data/path -name 'CDR*$file_date' -printf '"%TY-%Tm-%Td %TH:%TM:%.2TS\n"' |
        awk '\$0 > n { n = $0 } END { print n }'"
    

    Notice how the dollar sign in the Awk script needs to be backslash-escaped because the double quotes will cause anything with an unescaped dollar sign to be evaluated as a variable by the local shell before ssh runs.

    If you don't expect a lot of output, you could run the sort and head locally, in which case no quoting is required around the pipeline.

    ssh user@103.11.34.111 find /data/path -name "CDR*$file_date" -printf "'%TY-%Tm-%Td %TH:%TM:%.2TS\n'" |
    sort -rn | head -1
    

    ... but as I expect the find command to potentially produce a nontrivial amount of output, I have assumed you want to run the entire pipeline remotely, and only send the final result back over the ssh connection.

    I'll emphasize again that this only runs ssh find and then pipes the output back to your local shell, which runs the pipeline through sort and head locally.

    (And even then, the Awk script from above is probably an improvement over keeping all the lines you receive in memory at the same time so you can sort them.)

    Notice also how the argument to -printf needs to be doubly quoted (as in, both double and single quoted; or you could backslash-escape the space) to keep it as a single string from both the local and the remote shell.