List the files that I can read - bash

I would like to list any files that can be read by my current user in bash.
I'm not sure what would be the best way to check for that. I'm thinking something along the lines of ls -l | grep <myusername>|<mygroupname> or find ., but that doesn't deal with the other permissions.
Also, I'm working on NetBSD box.

Considering the 2 files below, where one can be read by user, and the other can't:
[fsilveir#fsilveir tmp]$ ls -l ./test_dir/can_read.txt ./test_dir/cant_read.txt
-rw-r--r--. 1 root root 861784 May 29 20:34 ./test_dir/can_read.txt
-rwx------. 1 root root 0 May 29 20:30 ./test_dir/cant_read.txt
You can use find with -perm option. By using +r you'll list the files you can read, and using -r for finding the ones you can't read, as shown below:
[fsilveir#fsilveir tmp]$ find . -name "*.txt" -perm -g+r 2>/dev/null
./test_dir/can_read.txt
[fsilveir#fsilveir tmp]$
Another approach is using find with -readable option, as shown below:
[fsilveir#fsilveir tmp]$ find . -name "*.txt" -readable
./test_dir/can_read.txt
[fsilveir#fsilveir tmp]$ find . -name "*.txt" ! -readable
./test_dir/cant_read.txt

Related

Basename removes the second file of my 'find' list in bash

I have run into an issue when using basename on a list of files found with find.
For some reason, basename decides to delete the second item of the find list when there are only two files to be found.
gas$ ls -l
total 8
-rwxrwxrwx 1 gas staff 54 Feb 26 19:00 find_sh.sh
-rw-r--r-- 1 gas staff 0 Feb 26 19:21 test-file.sh
gas$ find . -type f -name '*.sh'
./find_sh.sh
./test-file.sh
gas$ basename $(find . -type f -name '*.sh')
find_sh.sh
Adding a third file seems to fix it for an obscure reason (see below)
gas$ touch test-file2.sh
gas$ basename $(find . -type f -name '*.sh')
find_sh.sh
test-file.sh
test-file2.sh
Does anybody know what happens for the basename of the second file? I'm currious about what's going wrong.
I have found as a solution to ditch basename and simply use a sed 's/.*\///' to remove the path in the name.
So even if I found a solution, I'm still curious about what's going wrong with basename when there are two files to be displayed :)
Thanks in advance!
You don't need basename at all, find is capable on its own:
find . -type f -name '*.sh' -printf '%f\n'
The reason your 2 argument form of basename is not working like you expect is because that's how the 2 argument form of basename works. From the man page:
SYNOPSIS
basename NAME [SUFFIX]
basename OPTION... NAME...
When given no options, the 2nd argument is the suffix to strip off of the first argument.

Get file path with find without giving filename

How can I get the path of a .pid file that is inside a directory.
the code below returns only the file
root#linux [/]# ls -l $(find /* -name dovecot | grep var/run) | grep pid
-rw------- 1 root root 5 Nov 28 15:22 master.pid
Guess this is what you are looking for:
find /var/run -name "*.pid" 2>/dev/null | grep dovecot | xargs ls -l
You can also narrow the matches down in the grep command when you specify (parts of) the path inside the filter expression.
I think the interpretation of the output must be that the find command finds a directory name such as:
/var/run/dovecot
and you do an ls -l on the directory, which lists the files in the directory without any path leading to it. What you need is to find a reliable way of listing the files in the directory with their full path names.
One way — not I think a good way — of doing it would be:
find $(find /* -name dovecot -type d | grep var/run) -type f -name '*.pid' \
-exec ls -l {} +
This uses your first find command to get the directories you're interested in, then runs find again to find .pid files and execs ls -l on them. The + notation means that find behaves a bit like xargs, bunching a lot of file names together into a single run of ls -l.
cat /var/run/dovecot/master.pid
?
Or :
# readlink -f /var/run/dovecot/*.pid
/var/run/dovecot/master.pid

How to view file date of result of find command in bash

I use a find command to find some kinds of files in bash. Everything goes fine unlness the result that is shown to me just contains the file name but not the (last modification) date of file. I tried to pipe it into ls or ls -ltr but it just does not show the filedate column in result, also I tried this:
ls -ltr | find . -ctime 1
but actually I didn't work.
Can you please guide me how can I view the filedate of files returned by a find command?
You need either xargs or -exec for this:
find . -ctime 1 -exec ls -l {} \;
find . -ctime 1 | xargs ls -l
(The first executes ls on every found file individually, the second bunches them up into one ore more big ls invocations, so that they may be formatted slightly better.)
If all you want is to display an ls like output you can use the -ls option of find:
$ find . -name resolv.conf -ls
1048592 8 -rw-r--r-- 1 root root 126 Dec 9 10:12 ./resolv.conf
If you want only the timestamp you'll need to look at the -printf option
$ find . -name resolv.conf -printf "%a\n"
Mon May 21 09:15:24 2012
find . -ctime 1 -printf '%t\t%p\n'
prints the datetime and file path, separated by a ␉ character.

Using find command in bash script

I just start to use bash script and i need to use find command with more than one file type.
list=$(find /home/user/Desktop -name '*.pdf')
this code work for pdf type but i want to search more than one file type like .txt or .bmp together.Have you any idea ?
Welcome to bash. It's an old, dark and mysterious thing, capable of great magic. :-)
The option you're asking about is for the find command though, not for bash. From your command line, you can man find to see the options.
The one you're looking for is -o for "or":
list="$(find /home/user/Desktop -name '*.bmp' -o -name '*.txt')"
That said ... Don't do this. Storage like this may work for simple filenames, but as soon as you have to deal with special characters, like spaces and newlines, all bets are off. See ParsingLs for details.
$ touch 'one.txt' 'two three.txt' 'foo.bmp'
$ list="$(find . -name \*.txt -o -name \*.bmp -type f)"
$ for file in $list; do if [ ! -f "$file" ]; then echo "MISSING: $file"; fi; done
MISSING: ./two
MISSING: three.txt
Pathname expansion (globbing) provides a much better/safer way to keep track of files. Then you can also use bash arrays:
$ a=( *.txt *.bmp )
$ declare -p a
declare -a a=([0]="one.txt" [1]="two three.txt" [2]="foo.bmp")
$ for file in "${a[#]}"; do ls -l "$file"; done
-rw-r--r-- 1 ghoti staff 0 24 May 16:27 one.txt
-rw-r--r-- 1 ghoti staff 0 24 May 16:27 two three.txt
-rw-r--r-- 1 ghoti staff 0 24 May 16:27 foo.bmp
The Bash FAQ has lots of other excellent tips about programming in bash.
If you want to loop over what you "find", you should use this:
find . -type f -name '*.*' -print0 | while IFS= read -r -d '' file; do
printf '%s\n' "$file"
done
Source: https://askubuntu.com/questions/343727/filenames-with-spaces-breaking-for-loop-find-command
You can use this:
list=$(find /home/user/Desktop -name '*.pdf' -o -name '*.txt' -o -name '*.bmp')
Besides, you might want to use -iname instead of -name to catch files with ".PDF" (upper-case) extension as well.

How to only find files in a given directory, and ignore subdirectories using bash

I'm running the find command to find certain files, but some files in sub-directories have the same name which I want to ignore.
I'm interested in files/patterns like this:
/dev/abc-scanner, /dev/abc-cash ....
The command:
find /dev/ -name 'abc-*'
What's being returned:
/dev/abc-scanner
/dev/abc-cash
...
...
...
/dev/.udev/names/abc-scanner
/dev/.udev/names/abc-cash
I want to ignore the latter files: /dev/.udev/...
If you just want to limit the find to the first level you can do:
find /dev -maxdepth 1 -name 'abc-*'
... or if you particularly want to exclude the .udev directory, you can do:
find /dev -name '.udev' -prune -o -name 'abc-*' -print
Is there any particular reason that you need to use find? You can just use ls to find files that match a pattern in a directory.
ls /dev/abc-*
If you do need to use find, you can use the -maxdepth 1 switch to only apply to the specified directory.
This may do what you want:
find /dev \( ! -name /dev -prune \) -type f -print
I got here with a bit more general problem - I wanted to find files in directories matching pattern but not in their subdirectories.
My solution (assuming we're looking for all cpp files living directly in arch directories):
find . -path "*/arch/*/*" -prune -o -path "*/arch/*.cpp" -print
I couldn't use maxdepth since it limited search in the first place, and didn't know names of subdirectories that I wanted to exclude.
There is an alternative to find called rawhide (rh) and it's much easier to use. Instead of:
find /dev -maxdepth 1 -name 'abc-*'
You can do:
rh -r /dev '"abc-*"'
The -r is the same as "-m1 -M1" which is the same as find's "-mindepth 1 -maxdepth 1", just a lot shorter.
Rawhide (rh) is available from https://raf.org/rawhide or https://github.com/raforg/rawhide. It works at least on Linux, FreeBSD, OpenBSD, NetBSD, Solaris, macOS, and Cygwin.
Disclaimer: I am the current author of rawhide
find /dev -maxdepth 1 -name 'abc-*'
Does not work for me. It return nothing. If I just do '.' it gives me all the files in directory below the one I'm working in on.
find /dev -maxdepth 1 -name "*.root" -type 'f' -size +100k -ls
Return nothing with '.' instead I get list of all 'big' files in my directory as well as the rootfiles/ directory where I store old ones.
Continuing. This works.
find ./ -maxdepth 1 -name "*.root" -type 'f' -size +100k -ls
564751 71 -rw-r--r-- 1 snyder bfactory 115739 May 21 12:39 ./R24eTightPiPi771052-55.root
565197 105 -rw-r--r-- 1 snyder bfactory 150719 May 21 14:27 ./R24eTightPiPi771106-2.root
565023 94 -rw-r--r-- 1 snyder bfactory 134180 May 21 12:59 ./R24eTightPiPi77999-109.root
719678 82 -rw-r--r-- 1 snyder bfactory 121149 May 21 12:42 ./R24eTightPiPi771098-10.root
564029 140 -rw-r--r-- 1 snyder bfactory 170181 May 21 14:14 ./combo77v.root
Apparently /dev means directory of interest. But ./ is needed, not just .. The need for the / was not obvious even after I figured out what /dev meant more or less.
I couldn't respond as a comment because I have no 'reputation'.

Resources