Why using dirname in find command gives dots for each match?
Consider the following script:
#!/bin/sh
set -x
find `pwd` -name "file.ext" -exec echo $(dirname {}) \;
set -x
shows how the expansion works and what the final command is. When run, it gives the following output:
++ pwd
++ dirname '{}'
+ find /home/kibab -name file.ext -exec echo . ';'
So, the first thing that is expanded is the pwd
. Second is $(dirname {})
. The result of those two commands is then dropped into the find command. Thus, you're telling find to -exec echo .
, so you're seeing the expected output.
When you substitute basename
for dirname
, the expansion still takes places, but the results of the expansion are different:
pwd
is expanded to the current path. In my example above, the result is/home/kibab
basename {}
is executed. The result of this command is{}
.The find command is executed with the above substitutions in place. The final command executed looks like this:
find /home/kibab -name '*.png' -exec echo '{}' ';'
Upon inspecting the above command, you'll notice that the command now simply echo's whatever file was found.
Perhaps you want something like this?
find `pwd` -name "file.ext" -printf "%f\n"
Get only file name from find command and mail
all attachments in single mail:
find /home/cde -ctime -1 -name "Sum*pdf*" | while read name; do uuencode "$name" "${name##*/}"; done | mailx -s "subject" abc@example.com
to get separate mail:
find /home/cde -ctime -1 -name "Sum*pdf*" | while read name; do uuencode "$name" "${name##*/}"| mailx -s "subject" abc@example.com ; done
How to only get file name with Linux 'find'?
In GNU find
you can use -printf
parameter for that, e.g.:
find /dir1 -type f -printf "%f\n"
Regex for parsing directory and filename
Try this:
^(.+)\/([^\/]+)$
EDIT: escaped the forward slash to prevent problems when copy/pasting the Regex
Reliable way for a Bash script to get the full path to itself
Here's what I've come up with (edit: plus some tweaks provided by sfstewman, levigroker, Kyle Strand, and Rob Kennedy), that seems to mostly fit my "better" criteria:
SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
That SCRIPTPATH
line seems particularly roundabout, but we need it rather than SCRIPTPATH=`pwd`
in order to properly handle spaces and symlinks.
The inclusion of output redirection (>/dev/null 2>&1
) handles the rare(?) case where cd
might produce output that would interfere with the surrounding $( ... )
capture. (Such as cd
being overridden to also ls
a directory after switching to it.)
Note also that esoteric situations, such as executing a script that isn't coming from a file in an accessible file system at all (which is perfectly possible), is not catered to there (or in any of the other answers I've seen).
The --
after cd
and before "$0"
are in case the directory starts with a -
.
How do I get the directory where a Bash script is located from within the script itself?
#!/usr/bin/env bash
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
is a useful one-liner which will give you the full directory name of the script no matter where it is being called from.
It will work as long as the last component of the path used to find the script is not a symlink (directory links are OK). If you also want to resolve any links to the script itself, you need a multi-line solution:
#!/usr/bin/env bash
SOURCE=${BASH_SOURCE[0]}
while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
SOURCE=$(readlink "$SOURCE")
[[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
This last one will work with any combination of aliases, source
, bash -c
, symlinks, etc.
Beware: if you cd
to a different directory before running this snippet, the result may be incorrect!
Also, watch out for $CDPATH
gotchas, and stderr output side effects if the user has smartly overridden cd to redirect output to stderr instead (including escape sequences, such as when calling update_terminal_cwd >&2
on Mac). Adding >/dev/null 2>&1
at the end of your cd
command will take care of both possibilities.
To understand how it works, try running this more verbose form:
#!/usr/bin/env bash
SOURCE=${BASH_SOURCE[0]}
while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
TARGET=$(readlink "$SOURCE")
if [[ $TARGET == /* ]]; then
echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
SOURCE=$TARGET
else
DIR=$( dirname "$SOURCE" )
echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
SOURCE=$DIR/$TARGET # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
fi
done
echo "SOURCE is '$SOURCE'"
RDIR=$( dirname "$SOURCE" )
DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
if [ "$DIR" != "$RDIR" ]; then
echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"
And it will print something like:
SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
Getting the parent of a directory in Bash
dir=/home/smith/Desktop/Test
parentdir="$(dirname "$dir")"
Works if there is a trailing slash, too.
How do I exclude a directory when using `find`?
Use the -prune
primary. For example, if you want to exclude ./misc
:
find . -path ./misc -prune -o -name '*.txt' -print
To exclude multiple directories, OR them between parentheses.
find . -type d \( -path ./dir1 -o -path ./dir2 -o -path ./dir3 \) -prune -o -name '*.txt' -print
And, to exclude directories with a specific name at any level, use the -name
primary instead of -path
.
find . -type d -name node_modules -prune -o -name '*.json' -print
Test whether a glob has any matches in Bash
Bash-specific solution:
compgen -G "<glob-pattern>"
Escape the pattern or it'll get pre-expanded into matches.
Exit status is:
- 1 for no-match,
- 0 for 'one or more matches'
stdout
is a list of files matching the glob.
I think this is the best option in terms of conciseness and minimizing potential side effects.
Example:
if compgen -G "/tmp/someFiles*" > /dev/null; then
echo "Some files exist."
fi
Related Topics
Serial Port Doesn't Work Properly After Reboot, Unless I Execute Minicom
Minicom Black Background Color Is Not Respected
Trying to Search Files from User Keyword in Bash
Bash Script; How to Use Vars and Funcs Defined After Command
How to Preserve Command Line Spaces in a Linux Application
Different File Owner Inside Docker Container and in Host MAChine
How to Run a Mips Binary on X86 Platform
How to Copy All PDF Files from a Directory and Its Subdirectories to One Location
Linux Sed Command - Using Variable with Backslash
Pass Command-Line Arguments to Grep as Search Patterns and Print Lines Which Match Them All
Bash Variable Assignment Not Working Expected
Installed Clang++3.6 on Ubuntu, Can't Select as Alternative
Get Current Time in Hours and Minutes
How to Set Up Autocompletion for Git Commands
How to Open Sublime Text 2 Files from the Command Line in Linux to a Tab, Not a New Window
Unix Command to List Files Containing String But *Not* Containing Another String