You wrote (redundant parentheses added for clarity):
find zUnorganized/ \( -not -iname "*.mp3" -and -not -iname "*.flac" -and -not -iname "*.MP3" -and -not -iname "*.wav" \) \
-or \( -not -iname "*.m4a" -and -not -iname "*.jpg" \)
Either use -and -not
throughout, or use or
throughout and finish with -print
(meaning: do nothing for this, otherwise do nothing for that, etc, otherwise print). You'll also want to limit the search to regular files, otherwise directories will be listed.
find zUnorganized/ -type f -not -iname "*.mp3" -and -not -iname "*.flac" -and -not -iname "*.MP3" -and -not -iname "*.wav" -and -not -iname "*.m4a" -and -not -iname "*.jpg"
find zUnorganized/ \! -type f -o \
-iname "*.mp3" -o -iname "*.flac" -o -iname "*.MP3" -o -iname "*.wav" -o -iname "*.m4a" -o -iname "*.jpg" -o \
-print
Edit in response to updated question
Since you only care about links, directories and regular files, and don't need to deal with the other filetypes that ls
can identify (FIFOs, sockets etc), you could do something like stat
. For the examples below, I have created the following test environment:
$ ls -l
total 4.0K
-rw-r--r-- 1 terdon terdon 0 Jun 30 23:12 a new?line
-rw-r--r-- 1 terdon terdon 0 Jun 30 23:12 a space
-rw-r--r-- 1 terdon terdon 0 Jun 30 23:12 a?tab
drwxr-xr-x 2 terdon terdon 4.0K Jun 30 23:11 dir1
lrwxrwxrwx 1 terdon terdon 4 Jun 30 23:13 linktodir1 -> dir1
lrwxrwxrwx 1 terdon terdon 7 Jun 30 23:13 sh -> /bin/sh
As you can see, these include links, links to executables, a file name with a space, one with a tab (\t
) and one with a newline (\n
). Most of these files would break your ls
approach, but stat
can deal with them correctly:
$ stat --printf "%A\t%N\t%F\n" *
-rw-r--r-- ‘a new\nline’ regular file
-rw-r--r-- ‘a space’ regular file
-rw-r--r-- ‘a\ttab’ regular file
drwxr-xr-x ‘dir1’ directory
lrwxrwxrwx ‘linktodir1’ -> ‘dir1’ symbolic link
lrwxrwxrwx ‘sh’ -> ‘/bin/sh’ symbolic link
The relevant sections of man stat
:
--printf=FORMAT
like --format, but interpret backslash escapes, and do not output a mandatory trailing newline. If you want a newline, include \n in FORMAT
%A access rights in human readable form
%F file type
%N quoted file name with dereference if symbolic link
Note that fields are separated by \t
, this means you will be able to deal with whitespace within fields (in file names for example) gracefully.
You mentioned that you can't deal with ->
. I'm not entirely sure why, but you could either just remove that with sed
$ stat --printf "%A\t%N\t%F\n" * | sed 's/->//'
lrwxrwxrwx ‘linktodir1’ ‘dir1’ symbolic link
or substitute it with another string:
$ stat --printf "%A\t%N\t%F\n" * | sed 's/->/→/' | grep linktodir
lrwxrwxrwx ‘linktodir1’ → ‘dir1’ symbolic link
or just parse the file type.
Depending on what you want to do, it might be useful to separate each of the three file types you are searching for and deal with each separately. If so, use find
1 and its -printf
option:
$ find ./ -maxdepth 1 -mindepth 1 -type f -printf '%M\t%P\t%l\n' ## files
$ find ./ -maxdepth 1 -mindepth 1 -type d -printf '%M\t%P\t%l\n' ## directories
$ find ./ -maxdepth 1 -mindepth 1 -type l -printf '%M\t%P\t%l\n' ## links
In this case, the printf
directives are
%M File's permissions (in symbolic form, as for ls). This
directive is supported in findutils 4.2.5 and later.
%P File's name with the name of the command line argument
under which it was found removed.
%l Object of symbolic link (empty string if file is not a
symbolic link).
You could also combine the above into a single command (using find
's -o
operator) but which lets you use -printf
to print an arbitrary string depending on the file type. For example:
$ find ./ -maxdepth 1 -mindepth 1 \( -type l -printf 'link:\t%M\t%P\t%l\n' \) \
-o \( -type d -printf 'dir:\t%M\t%P\n' \) \
-o \( -type f -printf 'file:\t%M\t%P\n' \)
file: -rw-r--r-- a?tab
file: -rw-r--r-- a space
link: lrwxrwxrwx linktodir1 dir1
file: -rw-r--r-- a new?line
dir: drwxr-xr-x dir1
link: lrwxrwxrwx sh /bin/sh
The command above will interpret \t
and \n
correctly if its output is not shown on a terminal. However, to deal with file names with newlines correctly you will need to be careful when parsing (make sure a "line" begins with [file|dir|link]:
) or use \0
as a line terminator in each printf
call instead of \n
:
$ find ./ -maxdepth 1 -mindepth 1 \( -type l -printf 'link:\t%M\t%P\t%l\0' \) \
-o \( -type d -printf 'dir:\t%M\t%P\0' \) \
-o \( -type f -printf 'file:\t%M\t%P\0' \)
1 -maxdepth
and -mindepth
are GNU extensions, so this approach will only work for GNU find
.
The following were posted as solution to the first, less specific version of the question. I am leaving them here since they may be useful to others.
Shell and readlink
for f in *; do
readlink "$f" >/dev/null && echo "$(readlink -f "$f") (link)" || echo "$f";
done
Example output:
/etc (link)
foo
sample.R
sample.R~
The above iterates through all files and directories under the current one and if readlink
returns successful (if $f
is a link), it will dereference it (readlink -f
, note that this will follow all links. If you only want the first level, remove the -f
) and print the target along with (link)
. If it is not, it will just print $f
.
If this is just for you and not intended to be parsed, just use ls -l
:
$ ls -l
total 512116
-rw-r--r-- 1 terdon terdon 100641 Jun 30 19:10 er
lrwxrwxrwx 1 terdon terdon 5 Jun 30 19:12 etc -> /etc/
-rw-r--r-- 1 terdon terdon 524288000 Jun 30 19:10 foo
-rwxr--r-- 1 terdon terdon 353 Jun 30 15:22 sample.R
-rwxr--r-- 1 terdon terdon 249 Jun 30 14:51 sample.R~
That will clearly indicate links with link -> target
.
Best Answer
ls
itself won't show this information.You can pipe the output of the
find
tofile -f -
, as follows:Note that
find
is used instead ofls
as it will print the full path, whereasls
will only print the file name. Therefore, if you simply need to do this with the files in your current directory, then:would work.