0

I am new to shell scripting. I am seeking solution for it. I should have maintain the 10 files in my directory, Incase any number of file is added to this directory. I just want to move the oldest file to other directory. I gave naming Work for my directory and regular_archieve for another directory. I have tried this,

work = /home/balaji/work
regular_archieve = /home/balaji/regular_archieve
cd $work
for i in 'ls -t | sed -e '1,10d' | xargs -d '\n' rm -f'
do
 mv $i $regular_archieve
done
if [ls -t | wc -1 > 10 | mv /home/balaji/regular-archieve]
 then
   echo "more than 10 n files"
fi
mv $(ls -t /home/balaji/work | sed -e '1,10d' | tail - 1) /home/balaji/regular_archieve

But I am not able to move to file to another directory.

I am receiving this error,

mv: cannot stat 'file' : No such file or directory
  • I'd recommend debugging a bit to find the problem... (1) The error suggests something's the matter with the value of $i and/or $regular_archieve in the mv statement, so... (2) check the values of $i and $regular_archieve to see if they're what you expect, and if not... (3) try to determine why and fix the problem; then, if you're still stuck... (4) edit your question to ask for help based on the additional information. – David Yockey Sep 27 '19 at 11:53
  • What do you get when you stick the lines echo $i and echo $regular_archive into the do loop before the mv? – David Yockey Sep 27 '19 at 11:53
  • Please post the actual code that you are using. The code that you have in your question will not give the error that you mention, but a bash: work: command not found error followed by several other errors (mostly due to simple syntax errors). You may want to consider testing your code in https://www.shellcheck.net/ Do this while you are writing it, not after finishing your script. Also test each command as you write them down in the script so that you know they do what you'd expect them to do. – Kusalananda Sep 27 '19 at 15:25

3 Answers3

1

It's never a good idea to use the output of ls for anything except viewing in a terminal. See Why not parse ls (and what to do instead)?. Instead, use find:

find /home/balaji/work/ -type f -printf '%C@\t%p\0' |
  sort -z -k1,1 -r -n |
  cut -z -f2 |
  head -z -n 10 | 
  xargs -0r echo mv -t /home/balaji/regular_archive/

This requires the GNU versions of find, sort, cut, tail, and xargs (or, at least other versions of them that support the -z option for NUL record separators).

  • find uses -printf '%C@\t%p\0' to list the last-changed timestamps (%C@, in seconds since the epoch 1970-01-01 00:00:00) and filenames (%p) of all regular files. The fields are separated by a single tab (\t), and each record is separated by a NUL character (\0)
  • the output of find is piped into sort to reverse sort (-r) the files numerically (-n) on the first field only (-k 1,1) -- i.e. by the timestamp.
  • sort's output is piped into cut to delete the timestamp field (we no longer need it after we've finished sorting)
  • cut's output is piped into head to get the first ten entries
  • and finally, head's output is piped into xargs to run the mv command. This uses the GNU -t extension to mv, so that the target directory can be specified before the filenames.

Actually, this runs echo mv rather than mv, so it's a dry-run. Get rid of the echo when you're sure it's going to do what you want.

Note: This will work with any filenames, no matter what weird and annoying characters they might have in them (e.g. spaces, newlines, shell metacharacters, etc). Also, The file command has many other options which can be used to refine the search criteria.


If you have an old version of GNU coreutils (i.e. < version 8.25), neither cut nor head nor tail will have -z options. You can use awk instead. e.g.

find /home/balaji/work/ -type f -printf '%C@\t%p\0' |
  sort -z -k1,1 -r -n |
  awk -F '\t' 'BEGIN {RS=ORS="\0"}; NR<=10 { $1=""; $0=$0; $1=$1 ; print }' |
  xargs -0r echo mv -t /home/balaji/regular_archive/

Alternatively, you could use perl instead of awk:

 perl -F'\t' -0lane 'if ($. <= 10) {delete $F[0]; print @F}'
cas
  • 78,579
  • Thanks @cas for the answer and clear explanation along with, but still the files hasn't moved to another directory. I have received error like, mv -t /home/balaji/regular_archieve/ home/balaji/work/file15 Tue Sep 24 22:39:01.0895454669 2017@ – Balaji_Pisces Sep 27 '19 at 13:50
  • does /home/balaji/regular_archieve/ exist? even with the mis-spelling? 2. That output is not possible with the find command in my answer. What did you do that was different? Did you try to pipe ls into it instead of using find? don't do that. 3. What version of find etc are you using? As mentioned, this requires GNU or similarly-capable versions. It won't work with other versions....including the versions found in, e.g., busybox or toybox
  • – cas Sep 27 '19 at 13:58
  • yes exists. 2. As you said know and then i didn't try by using ls. 3.find version is find (GNU findutils) 4.5.11 . Actually I am working in centos which is in VM VirtualBox.
  • – Balaji_Pisces Sep 27 '19 at 14:07
  • the fact remains that that error message is not possible with the find command in my answer. it outputs ONLY the timestamp (in time_t seconds since epoch format) and the filename, separated by a TAB. It does not produce nicely formatted date strings with month and day names. – cas Sep 27 '19 at 14:10
  • did you type lowercase %c@ instead of upper-case %C@ ? – cas Sep 27 '19 at 14:11
  • I typed uppercase %C@ and again I ran it. Now the error looks different, cut: invalid option -- 'z' Try 'cut --help' for more information head: invalid option -- 'z' Try 'cut --help' for more information – Balaji_Pisces Sep 27 '19 at 14:15
  • OK, you've made one typo. you might have made others. carefully check that the command you ran exactly matches what's in my answer. fix as necessary. put it in a script file to make editing easy. – cas Sep 27 '19 at 14:16
  • I have checked very well. This -z seems reason for error. Because the error is, cut: invalid option -- 'z' Try 'cut --help' for more information head: invalid option -- 'z' Try 'cut --help' for more information I tried cut --help command and I couldn't find any values instead of -z. – Balaji_Pisces Sep 27 '19 at 14:31
  • what version of cut do you have? I have cut (GNU coreutils) 8.30. according to the coreutils changelog, GNU cut has supported -z since Jan 2016. – cas Sep 27 '19 at 14:32
  • Mine cut version is cut (GNU coreutils) 8.22 and head version is also same. – Balaji_Pisces Sep 27 '19 at 14:36
  • you'll need to upgrade to at least coreutils 8.25 which was released on 2016-01-20 – cas Sep 27 '19 at 14:38
  • or use awk -v RS=$'\0' 'NR <=10 {print $2}' instead of cut z -f2 | head -z -n 10 (head and tail got -z options the same day that cut did) – cas Sep 27 '19 at 14:40
  • Again, it comes this error mv -t /home/balaji/regular_archieve/ home/balaji/work/file15 156934494 8954546690 – Balaji_Pisces Sep 27 '19 at 14:51
  • try the updated version at the bottom of my answer. and for file's sake, have the echo before the mv command until you're absolutely 100% sure that it's going to do what you want. – cas Sep 27 '19 at 15:01
  • This time its add up all the files in the error, mv -t /home/balaji/regular_archieve/ home/balaji/work/file15 156934494 8954546690 home/balaji/work/file14 156934494 8954546690 home/balaji/work/file13 156934494 8954546690 home/balaji/work/file12 156934494 8954546690 .........home/balaji/work/file1 156934494 8954546690. – Balaji_Pisces Sep 27 '19 at 15:20
  • have you got %t instead of \t in the middle of the printf format string? or \n instead of \0? that would be another typo. It has to be exactly '%C@\t%p\0' -- the timestamp, a TAB, the filename, and a NUL. – cas Sep 27 '19 at 15:24