0

To delete all but 2 of the latest files in a dir running on a remote Linux box I use :

cd /data
ls -1tr | head -n -2 | xargs -d '\n' rm -f --

I execute these commands manually after logging into the remote Linux box but I'm attempting to combine login and running of these commands into a single command.

I perform something similar using scp where I login and scp the files to local machine at dir /local/data :

scp -i LightsailDefaultKey-eu-west-2.pem -r ubuntu@xxx.xxx.xxx.xxx:/data/ /local/data

Is there a method to perform something similar to above where login and commands :

cd /data
ls -1tr | head -n -2 | xargs -d '\n' rm -f --

are executed in a single command on remote machine from local machine ?

blue-sky
  • 480
  • 1
    You've tagged with [tag:ssh] so you're aware of that command. What didn't work for you? – Chris Davies Sep 19 '19 at 15:56
  • 2
    You shouldn't parse output of ls but anyway you might be looking for this: ssh host 'cd /data ; ls -1tr ...' – Arkadiusz Drabczyk Sep 19 '19 at 15:57
  • 2
    Answering a slightly different question, but maybe rsync --remove-source-files to copy the files and then remove them after they have been transferred may be what you are looking for. – icarus Sep 19 '19 at 16:54
  • 1
    Don't Parse ls. Use find instead, e.g. with GNU versions of find, sort, head, and cut: ssh -i LightsailDefaultKey-eu-west-2.pem ubuntu@xxx.xxx.xxx.xxx 'find /data/ -maxdepth 1 -type f -printf "%Cs\t%p\0" | sort -z -nr | head -z -n 2 | cut -z -f2 | xargs -0r rm -f --'. Every program in that pipeline uses NUL-separated input, so will work with any filenames containing any characters. – cas Sep 20 '19 at 02:47

3 Answers3

2

Agree with other posters you shouldn't use ls for this kind of parsing, but since what you have works for what you're doing, can't you just use the -d switch in your ls command to get the full directory path?

No reason to change your working directory unless you're doing much more work. Note you'll need to add that wildcard.

ssh user@host "ls -d1tr /data/* | head -n -2 | xargs -d '\n' rm -f --"
Bolwerk
  • 181
1
  1. Use find rather than ls. See Why not parse ls (and what to do instead)?.

  2. wrap the commands you want to run on the remote system in quotes (but note that there may be difficulties with nested quotes) or in a here document.

e.g. using GNU versions of find, sort, head, and cut:

ssh -i LightsailDefaultKey-eu-west-2.pem ubuntu@xxx.xxx.xxx.xxx \
  'find /data/ -maxdepth 1 -type f -printf "%Ts\t%p\0" | sort -z -nr | 
     head -z -n 2 | cut -z -f2 | xargs -0r rm -f --'

Note that I used double-quotes around the printf format string instead of single-quotes. That's because they're embedded in the single-quotes wrapping the entire pipeline of commands executed by ssh. I could have used -printf '\''%Cs\t%p\0'\'' instead, but (as there's no risk of variable interpolation etc here) it's easier to just use double-quotes.

The -printf format prints the file's last-modified timestamp (in seconds since the epoch, easily sorted as a number by the sort -z -rn), a tab, and the file's full pathname. The cut removes the timestamp and the tab, leaving only the filename to pipe into xargs.

An alternative would be to use a heredoc:

ssh -i LightsailDefaultKey-eu-west-2.pem ubuntu@xxx.xxx.xxx.xxx <<-'EOF'
    find /data/ -maxdepth 1 -type f -printf '%Ts\t%p\0' | sort -z -nr | 
      head -z -n 2 | cut -z -f2 | xargs -0r rm -f --
EOF
cas
  • 78,579
  • thanks for your answer but the above commands failed with no error. It did work with this command based on your comment regards double quotes : ssh -i LightsailDefaultKey-eu-west-2.pem -r ubuntu@xxx.xxx.xxx.xxx 'cd /data/; ls -1tr | head -n -2 | xargs -d "\n" rm -f --' . So I needed to wrap \n in double quotes. – blue-sky Sep 20 '19 at 16:29
-1

You can't combine an scp command and shell commands in the same execution afaik. The best you can do is:

    scp -i LightsailDefaultKey-eu-west-2.pem -r ubuntu@xxx.xxx.xxx.xxx:/data/ /local/data && \
    ssh -i LightsailDefaultKey-eu-west-2.pem -r ubuntu@xxx.xxx.xxx.xxx:/data/ /local/data bash -c "cd /data; ls -1tr | head -n -2 | xargs -d '\n' rm -f --"

note that I use && to make sure the ssh doesn't run unless the scp succeeds - otherwise, you'll remove files you didn't sync yet.

erik258
  • 343
  • 1
  • 7
  • At the point where you've launched a shell on the ssh server, you can't then initiate an scp from that terminal. And there's no way from scp to initiate a shell. Correct me if I'm wrong here - these have to be independent network transactions. – erik258 Sep 20 '19 at 02:57