Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have an application which generates a couple of files in different directories at a regular interval and I need to check the latest files generated, i.e. the file which is generated in the most recent directory (created or updated). The name of the file remains the same in all directories, so I just want a simple script which will change the directory to the last modified subdirectory.

[Update] By seeing most of the answers, I think I should clarify that the directory names are dynamic (depends upon some user input & timestamp), so I can assume a directory pattern like dir[1,2,3] or similar

share|improve this question

4 Answers 4

up vote 2 down vote accepted

Depending on what you want exactly to do, and the structure of your files, other possibilites may be available, such as :

ls -1tq /dir/*/readme.txt | head -n 1

Returns the full name of the most recent readme.txt found in any subdirectory (not recursive) of /dir/

The usage of * as part of the path (not just as part of a filename) often being underknown, I thought it was worth mentioning it.

file=$( ls -1tq /dir/*/readme.txt | head -n 1 )
cd $( dirname "$file" )
share|improve this answer
    
It would be a little safer to use ls -1tq, but that still has problems with spaces in file names if it matters, and globs in that way need validation... But all of that is for edge cases, anyway. Still - how do you cd to it? You might wanna put it in a $(command sub)? –  mikeserv Jul 29 at 10:40
    
That was more a way to open the possibilities : maybe he has concluded he had to change directory, without being strictly necessary. But you're right : you can still make things like cd $( ls -1t /dir/*/readme.txt | head | basename ) –  Pierre-Olivier Vares Jul 29 at 11:37
    
answer edited to reflect these thoughts –  Pierre-Olivier Vares Jul 29 at 11:43
    
@Pierre-OlivierVares thanks for the answer. This definitely helps and serves the purpose. –  Jay Jul 30 at 9:21

You don't need any elaborate pipelines for this. Moreover, you don't need to find anything - you already know where each file is, you just don't know which of them is newest. This is easily handled.

To demo, here's my little test:

mkdir ./dir1 ./dir2 ./dir3

for d in 1 2 3
do  touch ./dir$d/samefile
    sleep 1
done

That creates the test set. Now, which is newest?

set -- ./dir[123]/samefile
while [ -e "$2" ]
do  [ "$1" -nt "$2" ] && 
    set -- "$@" "$1"
shift; done
printf %s\\n "$@"

That will tell me.

OUTPUT

./dir3/samefile

And I can switch around the order of the touch operation as much as I like - it'll give me the right information. All you need is that tiny little shell loop in a function like this:

newd() { ${2:+:} return 1
    while [ -e "$2" ] && [ -e "$1" ]
    do  [ "$1" -nt "$2" ] && 
        set -- "$@" "$1"
    shift; done
    ${2+false} cd "${1%/*}"
}

Note that this is a little different than the demo so it will reliably fail given fewer than two arguments or if any argument is not an actual pathname.

Now you can call it like:

newd ./path/to/file1 ./path/to/file2

...and so on - with as many arguments as you like.

Or, in my test case:

newd ./dir[123]/samefile ; pwd

OUTPUT

/home/mikeserv/test/dir3
share|improve this answer

For more portability, you can use perl:

$ perl -MList::Util=reduce -le '
    BEGIN { $dir = "." }
    opendir DIR,$dir;
    print shift @{(reduce {$a->[1] > $b->[1] ? $a : $b}
                   map {[$_,(stat($_))[9]]}
                   grep { -d and !/^..?$/ }
                   readdir DIR)}
'
.Skype

Change $dir to whatever directory you want to search.

How does it work

  • readdir return list of all files and directory in dir handle open by opendir
  • grep { -d and !/^..?$/ }: we get all directory, skip . and .. directory.
  • map {[$_,(stat($_))[9]]}: creat an array, its element is a array reference with two elements:
    • First is directory name
    • Second is modified time
  • reduce {$a->[1] > $b->[1] ? $a : $b: like a max function, we compare each element in array by the second element, which is the modified time.
  • @{...} de-reference the array, shift get the first element, which is directory name.
share|improve this answer
    
What does Skype do? –  mikeserv Jul 29 at 10:30
1  
@mikeserv: oh, it's the result return in my current working directory. –  Gnouc Jul 29 at 10:32
    
Cool. well done. –  mikeserv Jul 29 at 10:35
    
@Gnouc thanks for the alternate perl script, but I like the one line bash command line for this matter. –  Jay Jul 30 at 9:25
    
@Jay: one line bash in this case is more difficult and less portable. You can call this perl directly in your bash script. If you only want bash, try @mikeserv's newd() function. –  Gnouc Jul 30 at 9:27

Short answer: look into man find and the 'time' options. This will locate the file(s), then use the -printf option to display the path of the file(s), which then can be used for cd <path>


One way to use find and extract DIR of the last accessed file (within the last 24h) in current dir and its sub directories

lastAccDir="$(find 2>/dev/null . -type f -atime 1 -printf "%a~%h\n" | sort -r | head -n 1 | cut -d~ -f2)"

or the same with line continuation (more readable)

lastAccDir="$(
find 2>/dev/null . -type f \ -atime 1 -printf "%a~%h\n" \
 | sort -r \
 | head -n 1 \
 | cut -d~ -f2
)"

Note that this MAY result in an empty result due to -atime 1 (last 24h)

test ! -z "$lastAccDir" && cd "$lastAccDir"
then checks for non-empty result and moves into the dir if not empty.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.