1

I am needing some help with a script. I'm currently trying to select PDF file that meets a specific requirement in the file name in order to move it to a different location.

The filename format I'm trying to select from has separated sections like the example below:

I_XXX_PACK_6788669_6_9358869.pdf

What I am trying to do is select only files that have the last section in the file name (e.g. 9358869) and ignore all others pdf files in the directory.

Can it be done with Unix tools (POSIX find, sed, grep, etc.), my main issue right now is trying to only get only the one file that was 6 separated sections in its filename and ignoring all other files.

  • Also is it possible to only choose the files that were created 1hr prior to system time? I have use sed on other file types to check for time, but not on a pdf file. So I am not sure if that is possible or not
2
  • 3
    Most filesystems do not store in file meta the time of creation, so you should select different time as reference. Commented Feb 29 at 6:25
  • 5
    Is the creation time encoded in the file name or stored in the file contents? If so, what is that mapping? If not, would last modification time work instead of creation time? When you say "Can this be done with bash only" do you mean can it be done with just bash builtins or do you mean can it be done with mandatory Unix tools (POSIX find, sed, grep, etc.) called from a bash script, or do you mean with GNU coreutils tools called from a bash script or something else? Please edit your question to provide answers and show your attempt to solve your problem.
    – Ed Morton
    Commented Feb 29 at 14:57

2 Answers 2

3

You have files such as I_XXX_PACK_6788669_6_9358869.pdf and you want only those that match six _-separated sections with the last one being 9358869.

This will match at least six _-separated sections (the * is a wildcard that matches zero or more characters, including _) ending with 9358869.pdf:

*_*_*_*_*_9358869.pdf

You can look for files modified (but not created) within the last hour using find, but this extension is not POSIX:

find /path/to/directory -type f -mmin -60

To allow for POSIX you need to use -newer {file}, having set {file} to an appropriate age. POSIX doesn't provide a reliable way of setting a file's modification datetime to an hour in the past, but as you've tagged with we can use that:

printf -v curr '%(%s)T'
past=$(printf '%(%Y%m%d%H%M)T\n' "$((curr - 60*60))")
touch -t "$past" /path/to/timestamp

find /path/to/directory -type f -newer /path/to/timestamp

Finally, merging the two:

touch -t "$(printf -v curr '%(%s)T'; printf '%(%Y%m%d%H%M)T\n' "$((curr - 60*60))")" /path/to/timestamp
find /path/to/directory -type f -newer /path/to/timestamp -name '*_*_*_*_*_9358869.pdf'
0
2
find . -name '[!_]*_*_*_*_*_*[!_].pdf' ! -name '*_*_*_*_*_*_*' ! -name '*__*'

Would report the files (of any type¹) whose name ends in .pdf and contains 5 and only 5 _ characters and where each of the _-separated parts of the root name is not empty.

With some find implementations, files whose name cannot be decoded as text in the current locale will also be excluded.

To limit to those last modified within the last hour, as Chris says, the -newermt '1 hour ago', -mmin 60 or -mtime -1m supported by some implementations are not standard and POSIX find has no equivalent other than -newer some-file-with-a-last-modification-time-one-hour-ago.

The usual way to create one search reference time in a POSIX way is to do:

TZ=XXX0 touch -t "$(TZ=XXX1 date +%Y%m%d%H%M.%S)" some-file-with-a-last-modification-time-one-hour-ago

Where date is called in a timezone (which we name XXX though the name is irrelevant here) which we defined as being one hour behind UTC and touch interprets the timestamp produced by date as if it was UTC time (in a timezone also called XXX but which is 0 hour behind UTC) so ends up creating a one hour old file.

After which you can do:

find . -name '[!_]*_*_*_*_*_*[!_].pdf' \
       ! -name '*_*_*_*_*_*_*' \
       ! -name '*__*' \
       -newer some-file-with-a-last-modification-time-one-hour-ago

If as your tag suggests, you're not constrained to using POSIX sh syntax, then you could use zsh instead where all that can be done without the need of any external utility:

set -o extendedglob
print -rC1 -- **/([^_]##_)(#c5)_[^_]##.pdf(ND-.mh-1)

Where:

  • **/ matches any level of subdirectories (including 0)
  • [^_] matches any character other than _
  • x## matches one or more xes, same as x(#c1,).
  • x(#c5) matches exactly 5 xes
  • Nullglob expands to nothing if there's no match instead of reporting an error.
  • Dotglob includes hidden ones
  • . and mh-1 regular files and last modified less than one hour ago (or in the future), and - for that check to be done after symlink resolution.
  • print -rC1 -- prints its arguments raw and on 1 Column.

If you want to restrict the match to those files where the last 3 parts have to be decimal integer numbers, you can change it to:

set -o extendedglob
print -rC1 -- **/([^_]##_)(#c3)<->_<->_<->.pdf(ND-.mh-1)

Where <-> is the <1-20> form of number matching operator but here without boundaries so matches any decimal integer number (any sequence of one or more ASCII decimal digits which you could also write [0-9]##).


¹ you could add -type f to only consider the files of type regular (as opposed to fifos, devices, directories, pipes...) but note that it would also exclude symlinks to regular files. To include those, you'd need -xtype f, but that's also a non-standard (GNU) extension.

4
  • Thanks for the help, This worked the best when I was testing it. I still need to verfiy this part "-newer some-file-with-a-last-modification-time-one-hour-ago" If you like I can show the code that I will be using Commented Mar 1 at 21:58
  • I was looking at the TZ= approach with date and touch but I ended up running round in ever decreasing circles. Does your approach work for timezones that are away from GMT? Commented Mar 4 at 7:58
  • @ChrisDavies, that's completely independent from the user's timezone. The user's timezone is not seen by either date nor touch as we define TZ for both, and those TZ are only intended as a tool to create a one hour old file. That's totally independent from any outside timezone. A file one hour in the past is a file one hour in the past regardless of what that timestamp looks like in the user's timezone. That's why I use XXX to make it clear that where on the globe those timezones may be relevant if at all is irrelevant. Commented Mar 4 at 8:02
  • Thanks for the confirmation Commented Mar 4 at 17:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .