how to use find result as part of regex in bash shell - bash

I want to find out which directory doesn't have *.dsc file in it, so I had a try:
find . -type d -exec ls {}/*.dsc
the output is as below:
ls: connot access './abc/*.dsc': No such file or directory
I'm sure that there is a dsc file in abc/ directory.
Seems bash shell will treat "{}/*.dsc" as a string but not a regex, so I had another try:
find . -type d|xargs -I {} ls {}/*.dsc
but the result is the same.
How could I get the command work as I need?

Can you try this one out :
find . ! -iname "*.dsc" -type d
!: This is used to negate the match. It will list everything but files with .dsc extension.
-type d: This will fetch all the directories.

I wasn't able to use wildcards in find + ls, but the command below works.
find . -type d -not -path . | while read -r dir; do [ -f $dir/*\.dsc ] || echo $dir; done
It test separately whether a file *.dsc exists and echoes the directory otherwise.

Related

List only files based on their middle part using bash ls command

I am a noob in bash and have a very basic question about bash.
I have files like:
a_lsst_z1.5_000.txt
a_lsst_z1.5_001.txt
a_lsst90_z1.5_001.txt
a_lsst_mono_z1.5_000.txt
a_lsst_mono_z1.5_001.txt
a_lsst_mono90_z1.5_000.txt
a_lsst_mono90_z1.5_001.txt
and so on
I would like to list ONLY files having lsst not (lsst90 or lsst_mono
or lsst_mono90.
I have tried:
ls a_lsst_*.txt # but it gives all files
Required output:
a_lsst_z1.5_000.txt
a_lsst_z1.5_001.txt
How to get only lsst files?
Maybe just match the first character after _ as a number?
echo a_lsst_[0-9]*.txt
After your edit, you could just match the z1.5 part:
echo a_lsst_z1.5_*.txt
try this
ls -ltr a_lsst_z1.5_*.txt
If you want to use ls and exclude certain character, you can try:
ls a_lsst[^9m]*.txt
This will exclude lsst90 and lsst_mono etc files.
find . -iname "a_lsst_*.txt" -type f -printf %P\\n 2>/dev/null
gives:
a_lsst_mono90_z1.5_001.txt
a_lsst_z1.5_000.txt
a_lsst_z1.5_001.txt
a_lsst_mono_z1.5_000.txt
a_lsst_mono_z1.5_001.txt
a_lsst_mono90_z1.5_000.txt
and
find . -iname "a_lsst_z1*.txt" -type f -printf %P\\n 2>/dev/null
gives:
a_lsst_z1.5_000.txt
a_lsst_z1.5_001.txt
with find command in the current dir . and with -iname you'll get expected results when using the pattern a_lsst_*.txt or a_lsst_z1*.txt.
Use -type f to get only match on files (not dirs).
Use -printf with %P to get paths without ./ at the beginning and \\n to have them ended with a new line char.
2>/dev/null prevents from displaying any error including very common Permission denied when using find command.

Find directories not containing a file recursively

I want to find the directories which does not having any files.Below is the sample script which will write a sample file with all the directory names.
#!/bin/bash
CURRENT_DATE=`date +'%d%m%Y'`
Year=`date +%Y`
Temp_Path=/appinfprd/bi/infogix/IA83/InfogixClient/Scripts/IRP/
File_Path=/bishare/DLSFTP/
cd $File_Path
echo $Year
find /bishare/DLSFTP/$Year* -type d -exec ls -dlrt {} + > $Temp_Path/Vendors_Data_$CURRENT_DATE.txt
If I understand well, you're looking for empty directory.
You can use the -empty parameter of find command for that:
find /bishare/DLSFTP/$Year* -type d -empty

find with nested command reading blacklist

I have a script that recursively searches all directories for specific files or specific file endings.
These certain files I want to save the path in a description file.
Looks for example like this:
./org/apache/commons/.../file1.pom
./org/apache/commons/.../file1.jar
./org/apache/commons/.../file1.zip
and so on.
In a blacklist , I describe which file endings I want to ignore.
! -path "./.cache/*" ! -path "./org/*" ! -name "*.sha1" ! -name"*.lastUpdated"
and so on.
Now i want to read this blacklist file while the search to ignore the described files:
find . -type f $(cat blacklist) > artifact.descriptor
Unfortunately, the blacklist will not be included while the search.
When:
echo "find . -type f $(cat blacklist) > artifact.descriptor"
Result is as expected:
find . -type f ! -path "./.cache/*" ! -path "./org/*" ! -name "*.sha1" ! -name"*.lastUpdated" > artifact.descriptor
But it does not work or exclude the described files.
I tried with following command and it works, but i want to know why not with with find alone.
find . -type f | grep -vf $blacklist > artifact.descriptor
Hopefully someone can explain it to me :)
Thanks a lot.
As tripleee suggests, it is generally considered bad practice to store a command in a variable because it does not catch all the cornercases.
However you can use eval as a workaround
/tmp/test$ ls
blacklist test.a test.b test.c
/tmp/test$ cat blacklist
-not -name *.c -not -name *.b
/tmp/test$ eval "find . -type f "`cat blacklist`
./test.a
./blacklist
In your case I think it fails because the quotes in your blacklist file are considered as a literal and not as enclosing the patterns and I think it works if you remove them, but still it's probably not safe for other reasons.
! -path ./.cache/* ! -path ./org/* ! -name *.sha1 ! -name *.lastUpdated

A script that iterates over all files in folder

There is a script on a server that I need to run over all the files in a folder. To run this script over one file I use this shell script:
for input in /home/arashsa/duo-bokmaal/Bokmaal/DUO_BM_28042.txt ; do
name=$(basename "$input")
/corpora/bokm/tools/The-Oslo-Bergen-Tagger/./tag-lbk.sh "$input" > "/home/arashsa/duo-bokmaal-obt/$name"
done
I'm terrible at writing shell scripts, and have not managed to found out how to iterate over files. What I want it is to make the script iterate over all files in a given folder that end with .txt and not those that end with _metadata.txt. So I'm thinking I would give it the folder path as argument, make it iterate over all the files in that folder, and run script on files ending with .txt and not _metadata.txt
Use find and the exec option.
$ find /path/to/dir -exec <command here> \;
Each file or directory can be obtained by using {}.
Example usage: $ find . -exec echo {} \;, this will echo each file name recursively or directory name in the current directory. You can use some other options to further specify the desired files and directories you wish to handle. I will briefly explain some of them. Note that the echo is redundant because the output of find will automatically print but I'll leave it there to illustrate the working of exec. This being said, following commands yield the same result: $ find . -exec echo {} \; and $ find .
maxdepth and mindepth
Specifying the maxdepth and mindepth allows you to go as deep down the directory structure as you like. Maxdepth determines how many times find will enter a directory and mindepth determines how many times a directory should be entered before selecting a file or dir.
Example usages:
(1) listing only elements from this dir, including . (= current dir).
(2) listing only elements from current dir excluding .
(3) listing elements from root dir and all dirs in this dir
(1)$ find . -maxdepth 1 -exec echo {} \;
(2)$ find . -mindepth 1 -maxdepth 1 -exec echo {} \;
# or, alternatively
(2)$ find . ! -path . -maxdepth 1 -exec echo {} \;
(3)$ find / -maxdepth 2 -exec echo {} \;
type
Specifying a type option allows you to filter files or directories only, example usage:
(1) list all files in this dir
(2) call shell script funtion func on every directory in the root dir.
(1)$ find . -maxdepth 1 -type f -exec echo {} \;
(2)$ find / -maxdepth 1 -type d -exec func {} \;
name & regex
The name option allows you to search for specific filenames, you can also look for files and dirs using a regex format.
Example usage: find all movies in a certain directory
$ find /path/to/dir -maxdepth 1 -regextype sed -regex ".*\.\(avi\|mp4\|mkv\)"
size
Another filter is the file size, any file or dir greater than this value will be returned. Example usage:
(1) find all empty files in current dir.
(2) find all non empty files in current dir.
(1)$ find . -maxdepth 1 -type f -size 0
(2)$ find . -maxdepth 1 -type f ! -size 0
Further examples
Move all files of this dir to a directory tmp present in .
$ find . -type f -maxdepth 1 -exec mv {} tmp \;
Convert all mkv files to mp4 files in a dir /path/to/dir and child directories
$ find /path/to/dir -maxdepth 2 -regextype sed -regex ".*\.mkv" -exec ffmpeg -i {} -o {}.mp4 \;
Convert all your jpeg files to png (don't do this, it will take very long to both find them and convert them).
$ find ~ -maxdepth 420 -regextype sed -regex '.*\.jpeg' -exec mogrify -format png {} \;
Note
The find command is a strong tool and it can prove to be fruitful to pipe the output to xargs. It's important to note that this method is superior to the following construction:
for file in $(ls)
do
some commands
done,
as the latter will handle files and directories containing spaces the wrong way.
In bash:
shopt -s extglob
for input in /dir/goes/here/*!(_metadata).txt
do
...
done

Bulk rename directories with prefix Unix

I am trying to bulk rename directories with a prefix in Unix. Prefix like abc-
So if current directory is 123, I want to make it abc-123, etc
I've tried
for d in $(find . -name '*' -type d) ; do
mv $d $(echo $d | sed 's/$d/abc-$d/g')
done
but that doesn't work. Do very little shell scripting so any help would be appreciated.
rename command is not available
Thank you!
If I understand your question, you could do it with one line and find -exec like so,
find . -type d -depth -execdir mv {} abc-{} \;
Try:
for d in $(find . -depth -type d); do
b=$(basename $d)
p=$(dirname $d)
mv -v $d $p/abc-$b
done
Note that the -depth argument is really important: it ensures that the directories are processed from bottom to top, so that you rename child directories before their parents. If you don't do that then you'll end up trying to rename paths that no longer exist.
Also I recommend replacing line 4 with
echo "mv -v $d $p/abc-$b"
and running that version of the loop first so you can see what it will do before trying it for real.

Resources