UNIX find for finding file names not paired by - unix

Is there a simple way to recursively find all files in a directory hierarchy, that do not have a matching file with a different extension?
For example the directory has a bunch of files ending in .dat
I want to find the .dat files that do not have an accompanying .out file.
I have a while loop that checks each entry, but that is slow for long lists...
I am using GNU find.

Perhaps something like this?
find . -name "*.dat" -print | sort > column1.txt
find . -name "*.out" -print | sort > column2.txt
diff column1.txt column2.txt
I haven't tested it, but I think it's probably close to what you're asking for.

find . -name '*.dat' -printf "[ -f %p ] || echo %p\n" | sed 's/\.dat/.out/' | sh

I had to add a bunch of bells and whistles to the 1st solution, but that was a good start, thanks...
find . -print | grep -Fi '.dat' | grep -vFi '.dat.' | sort | sed -e 's/.dat//g' > column1.txt
find . -print | grep -Fi '.out' | grep -vFi '.out.' | sort | sed -e 's/.out//g' > column2.txt
sdiff -s column1.txt column2.txt | grep -F '<' | cut -f1 -d"<" > c12diff.txt

Related

Format find output into one field

I am doing a find command to get items sorted by access time:
$ find . -printf "%A+\t%p\n" | sort -r
The output looks like this:
2020-05-05+06:00:55.5569719990 ./form.py
2020-05-05+06:00:55.5569719990 ./amazon.js
2020-05-04+12:48:24.8209719990 ./historical.py
However, I would like to only show the filepath and remove the timestamp after it's been sorted like that, so getting:
./form.py
./amazon.js
./historical.py
What would be the best way to do this?
Like this:
find . -printf "%A+\t%p\n" | sort -r | cut -d$'\t' -f2-
Or if you want to use awk, you should use TAB as delimiter (if not, it breaks on files with spaces on filenames):
find . -printf "%A+\t%p\n" | sort -r | awk -F$'\t' '{print $2}'
You can use awk here if you want which will figure out the separator for you:
find . -printf "%A+\t%p\n" | sort -r | awk '{print $2}'
Note that it needs single quotes and fields are one-indexed.

Sort Matched Files by Last Modified and Timestamp

I need to look for files that match a certain pattern of characters, then find the most recent file and display it. The below code isn't quite getting me there but, I think I'm close.
Code:
find /home/weather/data/blend/ -type f -name "*.ctl" -printf '%Ts\t%p\n' | sort -nr | cut -f2
Here's a working solution:
find . -mmin -720 -type f -name "*.ctl" -exec ls -t {} \; | cut -c 3-

How to grep for files containing a specific word and pass the list of files as argument to second command?

grep rli "stringName" * | xargs <second_command> <list_of files>
will the above code work for the functionality mentioned?
I am a beginner to not sure how to use it.
You are just missing hyphen for options to grep. Following should work
grep -rli "stringName" * | xargs <second_command>
Considering above command cannot handle whitespace or weird characters in file names, more robust solution would be to use find
find . -type f -exec grep -qi "stringName" {} + -print0 | xargs -0 <second_command>
Or use -Z option with xargs -0
grep -rli "stringName" * -Z | xargs -0 <second_command>
Extending on jkshah's answer, which is already quite good.
find . -type f -exec grep -qi "regex" {} \; -exec "second_command" {} \;
This has the advantage of being more portable (-print0 and -0 are gnu extensions).
It executes the second command for each matching file in turn. If you want to execute with a list of all matching files at the end instead, change the last \; to +

Remove underscores from all filenames within a directory

I have a folder "model" with files named like:
a_EmployeeData
a_TableData
b_TestData
b_TestModel
I basically need to drop the underscore and make them:
aEmployeeData
aTableData
bTestData
bTestModel
Is there away in the Unix Command Line to do so?
This will correctly process files containing odd characters like spaces or even newlines and should work on any Unix / Linux distribution being only based on POSIX syntax.
find model -type f -name "*_*" -exec sh -c 'd=$(dirname "$1"); mv "$1" "$d/$(basename "$1" | tr -d _)"' sh {} \;
Here is what it does:
For each file (not directory) containing an underscore in its name under the model directory and its subdirectories, rename the file in place with all the underscores stripped out.
You can do this simply with bash.
for file in /path/to/model/*; do
mv "$file" "${file/_/}"
done
If you have rename command available then simply do
rename 's/_//' /path/to/model/*
for f in model/* ; do mv "$f" `echo "$f" | sed 's/_//g'` ; done
Edit: modified a few things thanks to suggestions by others, but I'm afraid my code is still bad for strange filenames.
maybe this:
find model -name "*_*" -type f -maxdepth 1 -print | sed -e 'p;s/_//g' | xargs -n2 echo mv
Decomposition:
find all plain files in the directory model what contains at least one underscore, and don't search subdirectories
with the sed make filename adjustments - replace the _ with nothing
also print the old name
fed the two filenames to xargs what will rename the files with mv
The above is for a dry-run. When satisfied, remove the echo before mv for actual rename.
Warning: Will not work if filename contains spaces. If you have GNU sed you can
find . -name "*_*" -maxdepth 1 -print0 | sed -z 'p;s/_//g' | xargs -0 -n2 echo mv
and will works with a filenames with spaces too...
In zsh:
autoload zmv # in ~/.zshrc
cd model && zmv '(**/)(*)' '$1${2//_}'
marc#panic:~$ echo 'a_EmployeeData' | tr -d '_'
aEmployeeData
I had the same problem on my machine, but the filenames had more than one underscore. I used rename with the g option so that all underscores get removed:
find model/ -maxdepth 1 -type f | rename 's/_//g'
Or if there are no subdirectories, just
rename 's/_//g'
If you don't have rename, see Jaypal Singh's answer.
Use the global flag /g with your replace pattern to replace all occurrences within the filename.
find . -type f -print0 | xargs -0 rename 's/_//g'
Or if you want underscores replaced with spaces then use this:
find . -type f -print0 | xargs -0 rename 's/_/ /g'
If you like to live dangerously add the force flag -f in front of your replace pattern rename -f 's/_//g'

Pipe output to parameter

So I wanted to write a simple command that counts one less than the number of files in my current directory. I have this command that comes close but is off by one.
ls | wc -l
How can I pipe this to bc so I can subtract it by one?
Thanks!
To pipe to bc you could use something like this
echo " $(ls | wc -l) - 1 " | bc
EDIT: replace the part in the $( ) with steve's answer, or any other command you need.
That's really not what you want to do. Use find instead:
find . -maxdepth 1 -type f | wc -l
Also, you can exclude hidden files, with:
find . -maxdepth 1 -type f ! -name ".*" | wc -l
For completeness, you can handle files containing newlines and spaces like:
find . -maxdepth 1 -type f -print0 | tr -dc '\0' | wc -c

Resources