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

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.

Related

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

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.

Shell - modifying the find command output

Is there anyway to print and output to a find command without listing the directory of the find in your output? meaning if I issue
find /home/people/name -type f -name >> /output/log/output.txt
the output in the log is written as:
/home/people/name/filename1.txt
/home/people/name/filename2.txt
/home/people/name/filename3.txt
what I want is the just the file name without the directory name? is that possible?
By default, as you saw, find prints full paths:
$ find /home/people/name -type f
/home/people/name/filename1.txt
/home/people/name/filename3.txt
/home/people/name/filename2.txt
find, however, does offer control over the output using -printf. To get just the filename, with no path, try:
$ find /home/people/name -type f -printf '%f\n'
filename1.txt
filename3.txt
filename2.txt
%f tells find that you want the filename without the path. \n tells find that you want a newline after each filename.
The output can, of course, be saved in a file:
$ find /home/people/name -type f -printf '%f\n' >output.txt
$ cat output.txt
filename1.txt
filename3.txt
filename2.txt

Finding all PHP files within a certain directory containing a string

Im wondering if someone can help me out.
Im currently using the following to find all PHP files in a certain directory
find /home/mywebsite -type f -name "*.php"
How would i extend that to search through those PHP files and get all files with the string base64_decode?
Any help would be great.
Cheers,
find /home/mywebsite -type f -name '*.php' -exec grep -l base64_decode {} +
The -exec option to find executes a command on the files found. {} is replaced by the filename, and the + means that it should keep repeating this for all the filenames. grep looks for a string in the file, and the -l option tells it to print just the filename when there's a match, not all the matching lines.
If you're getting an error from find, you may have an old version that doesn't support the + feature of -exec. Use this command instead:
find /home/mywebsite -type f -name '*.php' | xargs grep -l base64_decode
xargs reads its standard input and turns them into arguments for the command line in its arguments.

How do I find all files that do not begin with a given prefix in bash?

I have a bunch of files in a folder:
foo_1
foo_2
foo_3
bar_1
bar_2
buzz_1
...
I want to find all the files that do not start with a given prefix and save the list to a text file. Here is an example for the files that do have a given prefix:
find bar_* > Positives.txt
If you're doing subdirectories as well:
find . ! -name "bar_*"
Or, equivalently,
find . -not -name "*bar_*"
This should do the trick in any shell
ls | grep -v '^prefix'
The -v option inverts grep's search logic, making it filter out all matches.
Using grep instead of find you can use powerful regular expressions instead of the limited glob patterns.
You want to find filenames not starting with bar_*?
recursive:
find ! -name 'bar_*' > Negatives.txt
top directory:
find -maxdepth 1 ! -name 'bar_*' > Negatives.txt
With extended globs:
shopt -s extglob
ls !(bar_*) > filelist.txt
The !(pattern) matches anything but pattern, so !(bar_*) is any filename that does not start with bar_.
Using bash and wildcards: ls [!bar_]*. There is a caveat: the order of the letters is not important, so rab_something.txt will not be listed.
In my case I had an extra requirement, the files must end with the .py extension. So I use:
find . -name "*.py" | grep -v prefix_
In your case, to just exclude files with prefix_:
find . | grep -v prefix_
Note that this includes all sub-directories. There are many ways to do this, but it can be easy to remember for those already familiar with find and grep -v which excludes results.

Find all files in a directory that are not directories themselves

I am looking for a way to list all the files in a directory excluding directories themselves, and the files in those sub-directories.
So if I have:
./test.log
./test2.log
./directory
./directory/file2
I want a command that returns: ./test.log ./test2.log and nothing else.
If you want test.log, test2.log, and file2 then:
find . -type f
If you do not want file2 then:
find . -maxdepth 1 -type f
If you need symlinks, pipes, device files and other specific elements of file system to be listed too, you should use:
find -maxdepth 1 -not -type d
This will list everything except directories.
using find is simple as:
find . -maxdepth 1 -type f
find only regular files
Use the -type f option with find to find only regular files. OR, to be even more-inclusive, use -not -type d to find all file types except directories.
When listing all files, I like to also sort them by piping to sort -V, like this:
# find only regular files
find . -type f | sort -V
# even more-inclusive: find all file types _except_ directories
find . -not -type d | sort -V
From man find:
-type c
File is of type c:
b - block (buffered) special
c - character (unbuffered) special
d - directory
p - named pipe (FIFO)
f - regular file
l - symbolic link; this is never true if the -L option or the -follow option is in effect, unless the symbolic link is broken. If you want to search for symbolic links when -L is in effect, use -xtype.
s - socket
D - door (Solaris)
To search for more than one type at once, you can supply the combined list of type letters separated by a comma , (GNU extension).
How to store the output of find (a multi-line string list of files) into a bash array
To take this one step further, here is how to store all filenames into a bash indexed array called filenames_array, so that you can easily pass them to another command:
# obtain a multi-line string of all filenames
filenames="$(find . -type f | sort -V)"
# read the multi-line string into a bash array
IFS=$'\n' read -r -d '' -a filenames_array <<< "$filenames"
# Now, the the variable `filenames_array` is a bash array which contains the list
# of all files! Each filename is a separate element in the array.
Now you can pass the entire array of filenames to another command, such as echo for example, by using "${filenames_array[#]}" to obtain all elements in the array at once, like this:
echo "${filenames_array[#]}"
OR, you can iterate over each element in the array like this:
echo "Files:"
for filename in "${filenames_array[#]}"; do
echo " $filename"
done
Sample output:
Files:
./file1.txt
./file2.txt
./file3.txt
References:
I was reminded of find . -type f from the main answer by #John Kugelman here.
I borrowed the part to read the multi-line string into a bash array from my own answer here: How to read a multi-line string into a regular bash "indexed" array
find . -type f
find /some/directory -type f
$ find . -type f -print
Each file will be on its own line. You must be in the directory you want to search.
One more option
ls -ltr | grep ^-
to list all files, while
ls -ltr | grep ^d
to list all directories

Resources