find order directories first, files last - linux

I'm trying to list all files using find, so that the directories are listed first (in order) and files at same depth are listed after:
test/test1/1.txt
test/test2/1.txt
test/xtest/1.txt
test/test.txt
I tried using this:
find -type f -printf "%d\t%p\n" | sort -nr
But it gives me this result:
test/xtest/1.txt
test/test2/1.txt
test/test1/1.txt
test/test.txt
Is there a way using find or should I look for something else?

Sort by filename first, then - by depth:
find . -type f -printf "%d %p\n" | sort -k2 | sort -k1,1nr

Related

UNIX: Use a single find command to search files larger than 4 MiB, then pipe the output to a sort command

I currently have a question I am trying to answer below. Below is what I have come up with, but doesn't appear to be working:
find /usr/bin -type f -size +4194304c | sort -n
Am I on the right track with the above?
Question:
Use a single find command to search for all files larger than 4 MiB in
/usr/bin, printing the listing in a long format. Pipe this output to a sort command
which will sort the list from largest to smallest
I'd fiddle with for -printf command line switch, sth like this:
find YOUR_CONDITION_HERE -printf '%s %p\n' | sort -n: %s stands for size in bytes, %p for file name.
You can trim the sizes later, e.g. using cut, e.g.:
find -type f -size +4194304c -printf '%s %p\n' | sort -n | cut -f 2 -d ' '
But given the fact you need the long list format, I guess you'll be adding more fields to printf's argument.
Related topic: https://superuser.com/questions/294161/unix-linux-find-and-sort-by-date-modified
You are on the right track, but the find command will only output the name of the file, not it's size. This is why sort will sort them alphabetically.
To sort by size, you can output the file list and then pass it to ls with xargs like this:
find /usr/bin -type f -size +4194304c | xargs ls -S
If you want ls to output the file list on a single column, you can replace the -S with -S1. The command would become:
find /usr/bin -type f -size +4194304c | xargs ls -S1
To make your command resistant to all filenames, I would suggest using -print0 (it will separate paths with the null character which is the only one that cannot appear in a filename in Linux). The command would become:
find /usr/bin -type f -size +4194304c -print0 | xargs -0 ls -S1
You could also try
find /usr/bin -type f -size +4194304c -ls | sort -n -k7
and if you want the results reversed then try
find /usr/bin -type f -size +4194304c -ls | sort -r -n -k7
Or another option
find /usr/bin -type f -size +4194304c -exec ls -lSd {} +

How to use GNU find command to find files by pattern and list files in order of most recent modification to least?

I want to use the GNU find command to find files based on a pattern, and then have them displayed in order of the most recently modified file to the least recently modified.
I understand this:
find / -type f -name '*.md'
but then what would be added to sort the files from the most recently modified to the least?
find can't sort files, so you can instead output the modification time plus filename, sort on modification time, then remove the modification time again:
find . -type f -name '*.md' -printf '%T# %p\0' | # Print time+name
sort -rnz | # Sort numerically, descending
cut -z -d ' ' -f 2- | # Remove time
tr '\0' '\n' # Optional: make human readable
This uses \0-separated entries to avoid problems with any kind of filenames. You can pass this directly and safely to a number of tools, but here it instead pipes to tr to show the file list as individual lines.
find <dir> -name "*.mz" -printf "%Ts - %h/%f\n" | sort -rn
Print the modified time in epoch format (%Ts) as well as the directories (%h) and file name (%f). Pipe this through to sort -rn to sort in reversed number order.
Pipe the output of find to xargs and ls:
find / -type f -name '*.md' | xargs ls -1t

Bash script that writes subdirectories who has more than 5 files

while I was trying to practice my linux skills, but I could not solve this question.
So its basically saying "Write a bash script that takes a name of
directory as a command argument and printf the name of subdirectories
that has more than 5 files in it."
I thought we will use the find command but ı still could not figure it out. My code is:
find directory -type d -mindepth5
but it's not working.
You can use find twice:
First you can use find and wc to count the number of files in a given directory:
nb=$(find directory -maxdepth 1 -type f -printf "x\n" | wc -l)
This just asks find to output an x on a line for each file in the directory directory, proceeding non-recursively, then wc -l counts the number of lines, so, really, nb is the number of files in directory.
If you want to know whether a directory contains more than 5 files, it's a good idea to stop find as soon as 6 files are found:
nb=$(find directory -maxdepth 1 -type f -printf "x\n" | head -6 | wc -l)
Here nb has an upper threshold of 6.
Now if for each subdirectory of a directory directory you want to output the number of files (threshold at 6), you can do this:
find directory -type d -exec bash -c 'nb=$(find "$0" -maxdepth 1 -type f -printf "x\n" | head -6 | wc -l); echo "$nb"' {} \;
where the $0 that appears is the 0-th argument, namely {} that find will replaced by the subdirectory of directory.
Finally, you only want to display the subdirectory name if the number of files is more than 5:
find . -type d -exec bash -c 'nb=$(find "$0" -maxdepth 1 -type f -printf "x\n" | head -6 | wc -l); ((nb>5))' {} \; -print
The final test ((nb>5)) returns success or failure whether nb is greater than 5 or not, and in case of success, find will -print the subdirectory name.
This should do the trick:
find directory/ -type f | sed 's/\(.*\)\/.*/\1/g' | sort | uniq -c | sort -n | awk '{if($1>5) print($2)}'
Using mindpeth is useless here since it only lists directories at at least depth 5. You say you need subdirectories with more then 5 files in it.
find directory -type f prints all files in subdirectories
sed 's/\(.*\)\/.*/\1/g' removes names of files leaving only list of subdirecotries without filenames
sort sorts that list so we can use uniq
uniq -c merges duplicate lines and writes how many times it occured
sort -n sorts it by number of occurences (so you end up with a list:(how many times, subdirectory))
awk '{if($1>5) print($2)}' prints only those with first comlun 1 > 5 (and it only prints the second column)
So you end up with a list of subdirectories with at least 5 files inside.
EDIT:
A fix for paths with spaces was proposed:
Instead of awk '{if($1>5) print($2)}' there should be awk '{if($1>5){ $1=""; print(substr($0,2)) }}' which sets first part of line to "" and then prints whole line without a leading space (which was delimiter). So put together we get this:
find directory/ -type f | sed 's/\(.*\)\/.*/\1/g' | sort | uniq -c | sort -n | awk '{if($1>5){ $1=""; print(substr($0,2)) }}'

How to find the latest executable file in a directory

I'm on linux an I want to know how to find the latest executable file in a directory?
I already know how to find the latest with:
ls -rt1 | tail -1
but how to filter out executable files?
EDIT:
I found a solution:
find path/to/dir/myfile* -perm /u=x,g=x,o=x -mtime 0 | tail -1
is this save? or is there a better solution??
Given the basic find command to look for files starting on current directory:
find . -type f
Let's add functionalities:
To find executables you can use the -executable option:
find . -type f -executable
To just find on one level of depth, that is, not within subdirectories, use the -maxdepth 1 option:
find . -maxdepth 1 -type f
To find last modified file in a directory, you can use How to recursively find the latest modified file in a directory?:
find . -type f -printf '%T# %p\n' | sort -n | tail -1 | cut -f2- -d" "
All together, this looks for last modified executable file in one level depth:
find . -maxdepth 1 -type f -executable -printf '%T# %p\n' | sort -n | tail -1 | cut -f2- -d" "

linux find command operation

My shell script find all files 90 days older
find /var/www/html/zip/data/*/*/*/*/* -type f -mtime +90
that returns the output like
/var/www/html/zip/data/2011/jan/11/333333/Photos/a.jpeg
/var/www/html/zip/data/2011/jan/11/333333/Photos/b.jpeg
/var/www/html/zip/data/2011/jan/11/333333/Photos/c.jpeg
/var/www/html/zip/data/2011/feb/11/333333/Photos/a.jpeg
/var/www/html/zip/data/2011/feb/11/333333/Photos/b.jpeg
What would i need to do to just fetch unique folder path from the above output using the same Find command so the output should be
/var/www/html/zip/data/2011/jan/11/333333/Photos
/var/www/html/zip/data/2011/feb/11/333333/Photos
So i believe there would need to append something in the above Find command but don't know what
Note: I would like to save the unique path in a variable
Try
find /var/www/html/zip/data/*/*/*/*/* -type f -mtime +90 -printf "%h\n" | sort | uniq
I am not sure if find can do this directly, but you could always use sed to post-process the results:
find /var/www/html/zip/data/*/*/*/*/* -type f -mtime +90 | sed 's|/[^/]*$||'
Piping the results further through uniq should remove duplicates (you might need to first do sort, but I doubt it).
You can find the solution this way
find /var/www/html/zip/data -type d -mtime +90 | uniq
The idea behind this is, whenever a file inside a folder is updated or modified, folder is also marked as modified. So in this case you will get all the folders which were not updated in last 90 days...
Adding to jonathanasdf's answer,
You could may be add a for loop,
$i=1;
for uniq_dir in `find /var/www/html/zip/data/*/*/*/*/* -type f -mtime +90 -printf "%h\n" | sort | uniq`;
do
a[$i]=$uniq_dir;
let "i = $i + 1";
done;

Resources