-2

Could use a bit of help guys.

I need to produce a report for the oldest and newest files that are residing in each unix directory

The structure is simple

Main folder with multiple subfolders that contains multiple files in each subfolder.

Folder
-Subfolders
--files

The below should output to txt file with following formatting.

SUBFOLDER_X
 OLDEST:Date:filename
 NEWEST:Date:filename
SUBFOLDER_Y
 OLDEST:Date:filename
 NEWEST:Date:filename

and so on...

I played with the below but stuck on getting it into shape of a report.


find . -maxdepth 1 -type f | cut -c3- |xargs ls -t | head -1

Any Help would be much appreciated.

Thanks

1 Answers1

2

You used find -maxdepth 1. I assume this is GNU find and other tools in your OS also support GNU extensions. This answer uses GNU find, GNU sort and GNU sed; it is not portable.

The following code is designed to handle all filenames (including filenames with newlines) reliably. Data is processed as null-terminated strings until finally tr converts nulls to newlines, so the output looks good¹ in a terminal. After the conversion the output may be ambiguous (because of newlines in filenames), but before the conversion it's not: each null-terminated line either begins with ./ (and then you know it's your SUBFOLDER_* line) or with OLDEST:/ NEWEST:. If you want to parse the output further, do it before tr and keep in mind you're dealing with null-terminated lines.

cd Folder and then:

#!/bin/sh

for d in .//; do ( cd "$d" && { printf '%s\0' "$d" find . -maxdepth 1 -type f -printf '%T@ %TF:%P\0'
| sort -zn | sed -z '1 p' | sed -zn ' 1 {s/[^ ]
/ OLDEST:/;p} $ {s/[^ ]* / NEWEST:/;p} '; } ) done | tr '\0' '\n'

Notes:

  • The shebang #!/bin/sh is to indicate the shell code is portable. I mean the shell code alone, the tools do use non-portable extensions. You don't need to run the code as a script, you don't need to run it strictly in sh, you can paste it into an interactive POSIX-compliant shell (e.g. Bash).

  • We use a subshell (( )), so the scope of cd is limited.

  • The trailing / in for d in ./*/ means we're iterating over directories (and symlinks to directories).

  • Remember * does not match names starting with a dot. Our code ignores hidden directories. Before you use .* (in case you want hidden directories), note it will match . and ...

  • ./ in for d in ./*/ is good for two reasons:

    1. We don't need -- later in cd "$d" (more info: Filenames with leading dashes).
    2. If a subdirectory is named OLDEST:…, it will appear as ./ OLDEST:… and you will be able to tell it's a SUBFOLDER_* line. Without ./ the line may look like a genuine OLDEST: line, unless you introduce an unambiguous prefix (e.g. printf 'DIR:%s\0' "$d").
  • In find I used -type f because you used it. I read the phrase "oldest and newest file" (in the title) according to the POSIX meaning of "file" which is broad. It seems you want regular files only.

  • Note our loop over subdirectories includes symlinks to directories, but -type f does not match symlinks to regular files. And while ./*/ ignores hidden directories, find does not ignore hidden files.

  • In find I used -maxdepth 1 because you used it. Along with -type f this means sub-subdirectories (if any) and their content will be totally ignored as if they didn't exist.

  • Adjust %TF to your needs.

  • A directory with no regular files will appear as a ./… line with no OLDEST:/ NEWEST: lines below.

  • A directory with exactly one regular file will appear with the file listed both as OLDEST: and NEWEST:. For this case to work we needed to duplicate the first line from find, this is what sed -z '1 p' does (and if there are more lines then this sed will ultimately not matter).


¹ The output is not sanitized though. Your terminal may still intercept and react to escape sequences, bell characters or anything it's configured to react to, if such sequences or characters appear in filenames. To view the output safely, pipe it to a pager like less.