I had n number of files are there in my ../in/ directory. From those files I want the oldest file to process and send it to the ../complete/ directory. The remaining files are sent to the ../error/ directory. Can anyone help?
3 Answers
To find oldest file and move that to your desired directory, use:
# cd to ../in/
$ ls -lt | grep -v '^d' | tail -1 | awk '{print $NF}' | xargs -I '{}' mv '{}' ../complete/
Now that your oldest file has been moved out, you can move all files to ../error/
$ mv -- * ../error/
The -- after mv is necessary if you have any file/directory names starting with a -.
- 13,589
To copy the oldest file to ../complete:
cp -v "$(find ../in -maxdepth 1 -type f -printf '%T@ %p\0' | sort -zn | \
sed -zn '1s/[0-9,\.]\+ //p')" ../complete
To copy the all except the oldest to ../error:
find ../in -maxdepth 1 -type f -printf '%T@ %p\0' | sort -zn | \
sed -zn '2,$s/[0-9,\.]\+ //p' | xargs -0 cp -vt ../error
Explanation:
find-maxdepth 1descends only 1 level; therefore it will not search the directory recusively.-type fmatch only files.-printf '...'a custom output format.%T@gives the file's last modification time in a timestamp,%pis the file's name. Notice the\0in the end. The files are printed null byte delimited, not newline delimited, because the newline could probably be a character in the file's name, which would then break the command.
sort -znthis will read the output null byte delimited (-z) and sort it numerically (-n).sed -znreads the output null byte delimited.1will only match the first line (first command) and2,$will match all others (second command)s/[0-9,\.]\+ //pwill remove the leading timestamp from the output.
- From the second command only:
xargs -0 cp -vt ../errorprocesses the output null byte delimited (-0) and callscp -vt ../erroron all input, which copies the files.
Example output:
$ ls -lt ../in/
total 0
-rw-r--r-- 1 user user 0 Jun 24 08:18 file2
-rw-r--r-- 1 user user 0 Jun 24 08:18 file3
-rw-r--r-- 1 user user 0 Jun 24 08:18 file4
-rw-r--r-- 1 user user 0 Jun 24 08:18 file5
-rw-r--r-- 1 user user 0 Jun 24 08:17 file_oldest
$ cp -v "$(...)" ../complete
'../in/file_oldest' -> '../complete/file_oldest'
$
$ find ... | xargs ... ../error
'../in/file2' -> '../error/file2'
'../in/file3' -> '../error/file3'
'../in/file4' -> '../error/file4'
'../in/file5' -> '../error/file5'
- 48,171
Building on Rahul’s answer:
$ ls -ltr | grep -v '^d' | awk 'NR==2 {print $NF; exit}' | xargs -I '{}' mv -- '{}' ../complete/
Notes:
- You say you want to do something with “the oldest file”. All the answers are assuming that you mean least recently modified. If you mean least recently changed, say so; the answers will be slightly different. If you mean least recently created, what you want is almost impossible in Unix.
ls -lt(what Rahul used) lists the current directory in long format in order by modification date/time, with the newest first and the oldest last.ls -ltris the reverse of that; oldest first and the newest last.- If you might have files whose names begin with a period (
.), add theAoption tols; e.g.,ls -ltrA. (The order of the options doesn’t matter; you can usels -ltAr,ls -lAtr, or evenls -ltr -Aif you want to.) - I lied.
A long
lslisting of a directory always has atotalline first. Inls -ltr, the oldest entry is on the second line. grep -v '^d'filters out the directories (if any).awk 'NR==2 { print $NF; exit }'prints the last field (the filename*) from the second line (the oldest file) and then exits.- This is one fewer process than
tail -1 | awk '{print $NF}'. - This might work faster, because,
when the
awkexits, thelsmight also exit, and you would avoid generating the listing for the entire directory. You can eliminate the
grepby doingls -ltr | awk '! /^d/ { count++ } count==2 { print $NF; exit }'but I wouldn’t advise it. Overly complex scripts are hard to maintain.
- This is one fewer process than
- The
--aftermvin thexargscommand is necessary if you have any file names starting with a-. You don’t really need the
xargs. You can simplify the above command to$ mv -- $(ls -ltr | grep -v '^d' | awk 'NR==2 { print $NF; exit }') ../complete/
________
* Like Rahul’s answer, this will fail if the filename contains space(s) or tab(s) — and even worse if it contains newline(s).
If you might have files whose names begin with a period (.),
do shopt -s dotglob before doing the
$ mv -- * ../error/