Finding all files with a given extension whose base name is the name of the parent directoryHow to exclude a list of full directory paths in find command on SolarisFind files that have a confirmed duplicate in same directory recursively'Tar' the result of a 'find', preserving the directory structureRename all files in directory while preserving any file extensionHow to find all files except with PNG, JPG extension?How to find and print specific file paths with exclusions?bash - Find All Files with Same Name Regardless of ExtensionFind all files with the same namefind: exclude n different directories and m different files present at any level but include few files from some excluded directoriesRenaming many files to their parent directory name
Multi tool use
What's the Difference between Two Single-Quotes and One Double-Quote?
Where is the encrypted mask value?
Why do they consider the Ori false gods?
I unknowingly submitted plagiarised work
What are these (utility?) boxes at the side of the house?
How long does it take to crack RSA 1024 with a PC?
What do different value notes on the same line mean?
Does this degree 12 genus 1 curve have only one point over infinitely many finite fields?
What is the difference between nullifying your vote and not going to vote at all?
Tic-tac-toe for the terminal, written in C
Mother abusing my finances
Crossing US border with music files I'm legally allowed to possess
Command to Search for Filenames Exceeding 143 Characters?
Why is this Simple Puzzle impossible to solve?
Employer demanding to see degree after poor code review
I think I may have violated academic integrity last year - what should I do?
Plot twist where the antagonist wins
How to capture more stars?
Why are these traces shaped in such way?
General purpose replacement for enum with FlagsAttribute
Which noble houses were destroyed during the Game of Thrones?
Windows 10 Programs start without visual Interface
How can I get exact maximal value of this expression?
Employer asking for online access to bank account - Is this a scam?
Finding all files with a given extension whose base name is the name of the parent directory
How to exclude a list of full directory paths in find command on SolarisFind files that have a confirmed duplicate in same directory recursively'Tar' the result of a 'find', preserving the directory structureRename all files in directory while preserving any file extensionHow to find all files except with PNG, JPG extension?How to find and print specific file paths with exclusions?bash - Find All Files with Same Name Regardless of ExtensionFind all files with the same namefind: exclude n different directories and m different files present at any level but include few files from some excluded directoriesRenaming many files to their parent directory name
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
I want to recursively look for every *.pdf
file in a directory ~/foo
whose base name matches the name of the file's parent directory.
For instance, suppose that the directory structure ~/foo
looks like this
foo
├── dir1
│ ├── dir1.pdf
│ └── dir1.txt
├── dir2
│ ├── dir2.tex
│ └── spam
│ └── spam.pdf
└── dir3
├── dir3.pdf
└── eggs
└── eggs.pdf
Running my desired command would return
~/foo/dir1/dir1.pdf
~/foo/dir2/spam/spam.pdf
~/foo/dir3/dir3.pdf
~/foo/dir3/eggs/eggs.pdf
Is this possible using find
or some other core utility? I assume this is doable using the -regex
option to find
but I'm not sure how to write the correct pattern.
find
add a comment |
I want to recursively look for every *.pdf
file in a directory ~/foo
whose base name matches the name of the file's parent directory.
For instance, suppose that the directory structure ~/foo
looks like this
foo
├── dir1
│ ├── dir1.pdf
│ └── dir1.txt
├── dir2
│ ├── dir2.tex
│ └── spam
│ └── spam.pdf
└── dir3
├── dir3.pdf
└── eggs
└── eggs.pdf
Running my desired command would return
~/foo/dir1/dir1.pdf
~/foo/dir2/spam/spam.pdf
~/foo/dir3/dir3.pdf
~/foo/dir3/eggs/eggs.pdf
Is this possible using find
or some other core utility? I assume this is doable using the -regex
option to find
but I'm not sure how to write the correct pattern.
find
Yes, I'll mock up an example now.
– Brian Fitzpatrick
May 21 at 5:10
1
@Inian Added an example. Does this help?
– Brian Fitzpatrick
May 21 at 5:15
add a comment |
I want to recursively look for every *.pdf
file in a directory ~/foo
whose base name matches the name of the file's parent directory.
For instance, suppose that the directory structure ~/foo
looks like this
foo
├── dir1
│ ├── dir1.pdf
│ └── dir1.txt
├── dir2
│ ├── dir2.tex
│ └── spam
│ └── spam.pdf
└── dir3
├── dir3.pdf
└── eggs
└── eggs.pdf
Running my desired command would return
~/foo/dir1/dir1.pdf
~/foo/dir2/spam/spam.pdf
~/foo/dir3/dir3.pdf
~/foo/dir3/eggs/eggs.pdf
Is this possible using find
or some other core utility? I assume this is doable using the -regex
option to find
but I'm not sure how to write the correct pattern.
find
I want to recursively look for every *.pdf
file in a directory ~/foo
whose base name matches the name of the file's parent directory.
For instance, suppose that the directory structure ~/foo
looks like this
foo
├── dir1
│ ├── dir1.pdf
│ └── dir1.txt
├── dir2
│ ├── dir2.tex
│ └── spam
│ └── spam.pdf
└── dir3
├── dir3.pdf
└── eggs
└── eggs.pdf
Running my desired command would return
~/foo/dir1/dir1.pdf
~/foo/dir2/spam/spam.pdf
~/foo/dir3/dir3.pdf
~/foo/dir3/eggs/eggs.pdf
Is this possible using find
or some other core utility? I assume this is doable using the -regex
option to find
but I'm not sure how to write the correct pattern.
find
find
edited May 22 at 7:39
user7761803
1433
1433
asked May 21 at 4:49
Brian FitzpatrickBrian Fitzpatrick
96721123
96721123
Yes, I'll mock up an example now.
– Brian Fitzpatrick
May 21 at 5:10
1
@Inian Added an example. Does this help?
– Brian Fitzpatrick
May 21 at 5:15
add a comment |
Yes, I'll mock up an example now.
– Brian Fitzpatrick
May 21 at 5:10
1
@Inian Added an example. Does this help?
– Brian Fitzpatrick
May 21 at 5:15
Yes, I'll mock up an example now.
– Brian Fitzpatrick
May 21 at 5:10
Yes, I'll mock up an example now.
– Brian Fitzpatrick
May 21 at 5:10
1
1
@Inian Added an example. Does this help?
– Brian Fitzpatrick
May 21 at 5:15
@Inian Added an example. Does this help?
– Brian Fitzpatrick
May 21 at 5:15
add a comment |
6 Answers
6
active
oldest
votes
With GNU find
:
find . -regextype egrep -regex '.*/([^/]+)/1.pdf'
-regextype egrep
use egrep style regex..*/
match grand parent directires.([^/]+)/
match parent dir in a group.1.pdf
usebackreference
to match file name as parent dir.
update
One (myself for one) might think that .*
is greedy enough, it's unnecessary to exclude /
from parent matching:
find . -regextype egrep -regex '.*/(.+)/1.pdf'
Above command won't work well, because it mathches ./a/b/a/b.pdf
:
.*/
matches./
(.+)/
matchesa/b/
1.pdf
matchesa/b.pdf
Very cool. Wish I could regex this well.
– Brian Fitzpatrick
May 21 at 5:19
Orfind . -regex '.*/([^/]*)/1.pdf'
and then it would even work with BSDfind
.
– Stéphane Chazelas
May 21 at 8:59
add a comment |
The traditional loop variant of the find .. -exec sh -c ''
to use the shell constructs to match the basename and the immediate path above would be to do below.
find foo/ -name '*.pdf' -exec sh -c '
for file; do
base="$file##*/"
path="$file%/*"
if [ "$path##*/" = "$base%.*" ]; then
printf "%sn" "$file"
fi
done' sh +
To breakdown the individual parameter expansions
file
contains the full path of the.pdf
file returned from thefind
command"$file##*/"
contains only the part after the last/
i.e. only the basename of the file"$file%/*"
contains the path up to the final/
i.e. except the basename portion of the result"$path##*/"
contains the part after the last/
from thepath
variable, i.e. the immediate folder path above the basename of the file"$base%.*"
contains the part of the basename with the.pdf
extension removed
So if the basename without extension matches with the name of the immediate folder above, we print the path.
add a comment |
The reverse of Inian's answer, i.e. look for directories, and then see whether they hold a file with a particular name.
The following prints the pathnames of the found files relative to the directory foo
:
find foo -type d -exec sh -c '
for dirpath do
pathname="$dirpath/$dirpath##*/.pdf"
if [ -f "$pathname" ]; then
printf "%sn" "$pathname"
fi
done' sh +
$dirpath##*/
will be replaced by the filename portion of the directory path, and could be replaced by $(basename "$dirpath")
.
For people who like the short-circuit syntax:
find foo -type d -exec sh -c '
for dirpath do
pathname="$dirpath/$dirpath##*/.pdf"
[ -f "$pathname" ] && printf "%sn" "$pathname"
done' sh +
The benefit of doing it this way is that you may have more PDF files than directories. The number of tests involved are reduced if one restrict the query by the smaller number (the number of directories).
For example, if a single directory contains 100 PDF files, this would only try to detect one of them rather than testing the names of all 100 files against that of the directory.
add a comment |
with zsh
:
printf '%sn' **/*/*.pdf(e@'[[ $REPLY:t = $REPLY:h:t.pdf ]]'@)
Beware that while **/
won't follow symlinks, */
will.
add a comment |
It was not specified, but here is a solution without regular expressions if anybody is interested.
We can use find . -type f
to just get files, then utilize dirname
and basename
to write the conditional. The utilities have the following behavior:
$ find . -type f
./dir2/spam/spam.pdf
./dir2/dir2.tex
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./dir1/dir1.txt
basename
returns just the filename after the last /
:
$ for file in $(find . -type f); do basename $file; done
spam.pdf
dir2.tex
dir3.pdf
eggs.pdf
dir1.pdf
dir1.txt
dirname
gives the entire path up to the final /
:
$ for file in $(find . -type f); do dirname $file; done
./dir2/spam
./dir2
./dir3
./dir3/eggs
./dir1
./dir1
Therefore, basename $(dirname $file)
gives the parent directory of the file.
$ for file in $(find . -type f); do basename $(dirname $file) ; done
spam
dir2
dir3
eggs
dir1
dir1
Solution
Combine the above to form the conditional "$(basename $file)" = "$(basename $(dirname $file))".pdf
, then only print each result from find
if that conditional returns true.
$ while read file; do if [ "$(basename "$file")" = "$(basename "$(dirname "$file")")".pdf ]; then echo $file; fi done < <(find . -type f)
./dir2/spam/spam.pdf
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./Final Thesis/grits/grits.pdf
./Final Thesis/Final Thesis.pdf
In the above example, we've added a directory/file with spaces in the name to treat that case (thanks to @Kusalananda in the comments)
This will unfortunately break on filenames likeFinal Thesis.pdf
(with a space).
– Kusalananda♦
May 21 at 16:17
@Kusalananda Fixed.
– user1717828
May 21 at 16:38
add a comment |
I take bash globbing, simple loop over string tests any day over the Find program. Call me irrational, and while it may well be suboptimal such simple code does the trick for me: readable and reusable, satisfying even!. Allow me therefore to suggest a combination of:
• bash globstar : for f in ** ; do ...
** loops over every files in the current directory and all subfolders.. to check globstar status in your current session: shopt -p globstar
. To activate globstar: shopt -s globstar
.
• "file" utlity : if [[ $(file "$f") =~ pdf ]]; then ...
to check actual file format for pdf - more robust than testing only for the file's extension
• basename, dirname :
to compare the file name to the name of the directory immediately above it. basename
returns the filename - dirname
returns entire directory path - combine the two functions to only return the one directory containing the matching file.
I put each one in a variable (_mydir and _myf) to then do a simple test using =~ for string matching.
One subtility: remove any "dot" in the filename to avoid matching filename to current directory whose shortcut is also "." - I used direct string substitution on the variable _myf : $_myf//./
- not very elegant but it works. Positive matches will return each file's path - together with the full path of the current folder by preceding the output with : $(pwd)/
.
Code
for f in ** ; do
if [[ $(file "$f") =~ PDF ]]; then
_mydir="$(basename $(dirname $f))" ;
_myf="$(basename $f)" ;
[[ "$_myf//./" =~ "$_mydir" ]] && echo -e "$(pwd)/$f" ;
fi ;
done
add a comment |
Your Answer
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "106"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f520078%2ffinding-all-files-with-a-given-extension-whose-base-name-is-the-name-of-the-pare%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
6 Answers
6
active
oldest
votes
6 Answers
6
active
oldest
votes
active
oldest
votes
active
oldest
votes
With GNU find
:
find . -regextype egrep -regex '.*/([^/]+)/1.pdf'
-regextype egrep
use egrep style regex..*/
match grand parent directires.([^/]+)/
match parent dir in a group.1.pdf
usebackreference
to match file name as parent dir.
update
One (myself for one) might think that .*
is greedy enough, it's unnecessary to exclude /
from parent matching:
find . -regextype egrep -regex '.*/(.+)/1.pdf'
Above command won't work well, because it mathches ./a/b/a/b.pdf
:
.*/
matches./
(.+)/
matchesa/b/
1.pdf
matchesa/b.pdf
Very cool. Wish I could regex this well.
– Brian Fitzpatrick
May 21 at 5:19
Orfind . -regex '.*/([^/]*)/1.pdf'
and then it would even work with BSDfind
.
– Stéphane Chazelas
May 21 at 8:59
add a comment |
With GNU find
:
find . -regextype egrep -regex '.*/([^/]+)/1.pdf'
-regextype egrep
use egrep style regex..*/
match grand parent directires.([^/]+)/
match parent dir in a group.1.pdf
usebackreference
to match file name as parent dir.
update
One (myself for one) might think that .*
is greedy enough, it's unnecessary to exclude /
from parent matching:
find . -regextype egrep -regex '.*/(.+)/1.pdf'
Above command won't work well, because it mathches ./a/b/a/b.pdf
:
.*/
matches./
(.+)/
matchesa/b/
1.pdf
matchesa/b.pdf
Very cool. Wish I could regex this well.
– Brian Fitzpatrick
May 21 at 5:19
Orfind . -regex '.*/([^/]*)/1.pdf'
and then it would even work with BSDfind
.
– Stéphane Chazelas
May 21 at 8:59
add a comment |
With GNU find
:
find . -regextype egrep -regex '.*/([^/]+)/1.pdf'
-regextype egrep
use egrep style regex..*/
match grand parent directires.([^/]+)/
match parent dir in a group.1.pdf
usebackreference
to match file name as parent dir.
update
One (myself for one) might think that .*
is greedy enough, it's unnecessary to exclude /
from parent matching:
find . -regextype egrep -regex '.*/(.+)/1.pdf'
Above command won't work well, because it mathches ./a/b/a/b.pdf
:
.*/
matches./
(.+)/
matchesa/b/
1.pdf
matchesa/b.pdf
With GNU find
:
find . -regextype egrep -regex '.*/([^/]+)/1.pdf'
-regextype egrep
use egrep style regex..*/
match grand parent directires.([^/]+)/
match parent dir in a group.1.pdf
usebackreference
to match file name as parent dir.
update
One (myself for one) might think that .*
is greedy enough, it's unnecessary to exclude /
from parent matching:
find . -regextype egrep -regex '.*/(.+)/1.pdf'
Above command won't work well, because it mathches ./a/b/a/b.pdf
:
.*/
matches./
(.+)/
matchesa/b/
1.pdf
matchesa/b.pdf
edited May 21 at 9:16
answered May 21 at 5:16
dedowsdidedowsdi
69416
69416
Very cool. Wish I could regex this well.
– Brian Fitzpatrick
May 21 at 5:19
Orfind . -regex '.*/([^/]*)/1.pdf'
and then it would even work with BSDfind
.
– Stéphane Chazelas
May 21 at 8:59
add a comment |
Very cool. Wish I could regex this well.
– Brian Fitzpatrick
May 21 at 5:19
Orfind . -regex '.*/([^/]*)/1.pdf'
and then it would even work with BSDfind
.
– Stéphane Chazelas
May 21 at 8:59
Very cool. Wish I could regex this well.
– Brian Fitzpatrick
May 21 at 5:19
Very cool. Wish I could regex this well.
– Brian Fitzpatrick
May 21 at 5:19
Or
find . -regex '.*/([^/]*)/1.pdf'
and then it would even work with BSD find
.– Stéphane Chazelas
May 21 at 8:59
Or
find . -regex '.*/([^/]*)/1.pdf'
and then it would even work with BSD find
.– Stéphane Chazelas
May 21 at 8:59
add a comment |
The traditional loop variant of the find .. -exec sh -c ''
to use the shell constructs to match the basename and the immediate path above would be to do below.
find foo/ -name '*.pdf' -exec sh -c '
for file; do
base="$file##*/"
path="$file%/*"
if [ "$path##*/" = "$base%.*" ]; then
printf "%sn" "$file"
fi
done' sh +
To breakdown the individual parameter expansions
file
contains the full path of the.pdf
file returned from thefind
command"$file##*/"
contains only the part after the last/
i.e. only the basename of the file"$file%/*"
contains the path up to the final/
i.e. except the basename portion of the result"$path##*/"
contains the part after the last/
from thepath
variable, i.e. the immediate folder path above the basename of the file"$base%.*"
contains the part of the basename with the.pdf
extension removed
So if the basename without extension matches with the name of the immediate folder above, we print the path.
add a comment |
The traditional loop variant of the find .. -exec sh -c ''
to use the shell constructs to match the basename and the immediate path above would be to do below.
find foo/ -name '*.pdf' -exec sh -c '
for file; do
base="$file##*/"
path="$file%/*"
if [ "$path##*/" = "$base%.*" ]; then
printf "%sn" "$file"
fi
done' sh +
To breakdown the individual parameter expansions
file
contains the full path of the.pdf
file returned from thefind
command"$file##*/"
contains only the part after the last/
i.e. only the basename of the file"$file%/*"
contains the path up to the final/
i.e. except the basename portion of the result"$path##*/"
contains the part after the last/
from thepath
variable, i.e. the immediate folder path above the basename of the file"$base%.*"
contains the part of the basename with the.pdf
extension removed
So if the basename without extension matches with the name of the immediate folder above, we print the path.
add a comment |
The traditional loop variant of the find .. -exec sh -c ''
to use the shell constructs to match the basename and the immediate path above would be to do below.
find foo/ -name '*.pdf' -exec sh -c '
for file; do
base="$file##*/"
path="$file%/*"
if [ "$path##*/" = "$base%.*" ]; then
printf "%sn" "$file"
fi
done' sh +
To breakdown the individual parameter expansions
file
contains the full path of the.pdf
file returned from thefind
command"$file##*/"
contains only the part after the last/
i.e. only the basename of the file"$file%/*"
contains the path up to the final/
i.e. except the basename portion of the result"$path##*/"
contains the part after the last/
from thepath
variable, i.e. the immediate folder path above the basename of the file"$base%.*"
contains the part of the basename with the.pdf
extension removed
So if the basename without extension matches with the name of the immediate folder above, we print the path.
The traditional loop variant of the find .. -exec sh -c ''
to use the shell constructs to match the basename and the immediate path above would be to do below.
find foo/ -name '*.pdf' -exec sh -c '
for file; do
base="$file##*/"
path="$file%/*"
if [ "$path##*/" = "$base%.*" ]; then
printf "%sn" "$file"
fi
done' sh +
To breakdown the individual parameter expansions
file
contains the full path of the.pdf
file returned from thefind
command"$file##*/"
contains only the part after the last/
i.e. only the basename of the file"$file%/*"
contains the path up to the final/
i.e. except the basename portion of the result"$path##*/"
contains the part after the last/
from thepath
variable, i.e. the immediate folder path above the basename of the file"$base%.*"
contains the part of the basename with the.pdf
extension removed
So if the basename without extension matches with the name of the immediate folder above, we print the path.
edited May 21 at 5:55
answered May 21 at 5:49
InianInian
6,0751633
6,0751633
add a comment |
add a comment |
The reverse of Inian's answer, i.e. look for directories, and then see whether they hold a file with a particular name.
The following prints the pathnames of the found files relative to the directory foo
:
find foo -type d -exec sh -c '
for dirpath do
pathname="$dirpath/$dirpath##*/.pdf"
if [ -f "$pathname" ]; then
printf "%sn" "$pathname"
fi
done' sh +
$dirpath##*/
will be replaced by the filename portion of the directory path, and could be replaced by $(basename "$dirpath")
.
For people who like the short-circuit syntax:
find foo -type d -exec sh -c '
for dirpath do
pathname="$dirpath/$dirpath##*/.pdf"
[ -f "$pathname" ] && printf "%sn" "$pathname"
done' sh +
The benefit of doing it this way is that you may have more PDF files than directories. The number of tests involved are reduced if one restrict the query by the smaller number (the number of directories).
For example, if a single directory contains 100 PDF files, this would only try to detect one of them rather than testing the names of all 100 files against that of the directory.
add a comment |
The reverse of Inian's answer, i.e. look for directories, and then see whether they hold a file with a particular name.
The following prints the pathnames of the found files relative to the directory foo
:
find foo -type d -exec sh -c '
for dirpath do
pathname="$dirpath/$dirpath##*/.pdf"
if [ -f "$pathname" ]; then
printf "%sn" "$pathname"
fi
done' sh +
$dirpath##*/
will be replaced by the filename portion of the directory path, and could be replaced by $(basename "$dirpath")
.
For people who like the short-circuit syntax:
find foo -type d -exec sh -c '
for dirpath do
pathname="$dirpath/$dirpath##*/.pdf"
[ -f "$pathname" ] && printf "%sn" "$pathname"
done' sh +
The benefit of doing it this way is that you may have more PDF files than directories. The number of tests involved are reduced if one restrict the query by the smaller number (the number of directories).
For example, if a single directory contains 100 PDF files, this would only try to detect one of them rather than testing the names of all 100 files against that of the directory.
add a comment |
The reverse of Inian's answer, i.e. look for directories, and then see whether they hold a file with a particular name.
The following prints the pathnames of the found files relative to the directory foo
:
find foo -type d -exec sh -c '
for dirpath do
pathname="$dirpath/$dirpath##*/.pdf"
if [ -f "$pathname" ]; then
printf "%sn" "$pathname"
fi
done' sh +
$dirpath##*/
will be replaced by the filename portion of the directory path, and could be replaced by $(basename "$dirpath")
.
For people who like the short-circuit syntax:
find foo -type d -exec sh -c '
for dirpath do
pathname="$dirpath/$dirpath##*/.pdf"
[ -f "$pathname" ] && printf "%sn" "$pathname"
done' sh +
The benefit of doing it this way is that you may have more PDF files than directories. The number of tests involved are reduced if one restrict the query by the smaller number (the number of directories).
For example, if a single directory contains 100 PDF files, this would only try to detect one of them rather than testing the names of all 100 files against that of the directory.
The reverse of Inian's answer, i.e. look for directories, and then see whether they hold a file with a particular name.
The following prints the pathnames of the found files relative to the directory foo
:
find foo -type d -exec sh -c '
for dirpath do
pathname="$dirpath/$dirpath##*/.pdf"
if [ -f "$pathname" ]; then
printf "%sn" "$pathname"
fi
done' sh +
$dirpath##*/
will be replaced by the filename portion of the directory path, and could be replaced by $(basename "$dirpath")
.
For people who like the short-circuit syntax:
find foo -type d -exec sh -c '
for dirpath do
pathname="$dirpath/$dirpath##*/.pdf"
[ -f "$pathname" ] && printf "%sn" "$pathname"
done' sh +
The benefit of doing it this way is that you may have more PDF files than directories. The number of tests involved are reduced if one restrict the query by the smaller number (the number of directories).
For example, if a single directory contains 100 PDF files, this would only try to detect one of them rather than testing the names of all 100 files against that of the directory.
edited May 21 at 15:40
answered May 21 at 6:58
Kusalananda♦Kusalananda
148k18279466
148k18279466
add a comment |
add a comment |
with zsh
:
printf '%sn' **/*/*.pdf(e@'[[ $REPLY:t = $REPLY:h:t.pdf ]]'@)
Beware that while **/
won't follow symlinks, */
will.
add a comment |
with zsh
:
printf '%sn' **/*/*.pdf(e@'[[ $REPLY:t = $REPLY:h:t.pdf ]]'@)
Beware that while **/
won't follow symlinks, */
will.
add a comment |
with zsh
:
printf '%sn' **/*/*.pdf(e@'[[ $REPLY:t = $REPLY:h:t.pdf ]]'@)
Beware that while **/
won't follow symlinks, */
will.
with zsh
:
printf '%sn' **/*/*.pdf(e@'[[ $REPLY:t = $REPLY:h:t.pdf ]]'@)
Beware that while **/
won't follow symlinks, */
will.
answered May 21 at 5:59
Stéphane ChazelasStéphane Chazelas
320k57606975
320k57606975
add a comment |
add a comment |
It was not specified, but here is a solution without regular expressions if anybody is interested.
We can use find . -type f
to just get files, then utilize dirname
and basename
to write the conditional. The utilities have the following behavior:
$ find . -type f
./dir2/spam/spam.pdf
./dir2/dir2.tex
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./dir1/dir1.txt
basename
returns just the filename after the last /
:
$ for file in $(find . -type f); do basename $file; done
spam.pdf
dir2.tex
dir3.pdf
eggs.pdf
dir1.pdf
dir1.txt
dirname
gives the entire path up to the final /
:
$ for file in $(find . -type f); do dirname $file; done
./dir2/spam
./dir2
./dir3
./dir3/eggs
./dir1
./dir1
Therefore, basename $(dirname $file)
gives the parent directory of the file.
$ for file in $(find . -type f); do basename $(dirname $file) ; done
spam
dir2
dir3
eggs
dir1
dir1
Solution
Combine the above to form the conditional "$(basename $file)" = "$(basename $(dirname $file))".pdf
, then only print each result from find
if that conditional returns true.
$ while read file; do if [ "$(basename "$file")" = "$(basename "$(dirname "$file")")".pdf ]; then echo $file; fi done < <(find . -type f)
./dir2/spam/spam.pdf
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./Final Thesis/grits/grits.pdf
./Final Thesis/Final Thesis.pdf
In the above example, we've added a directory/file with spaces in the name to treat that case (thanks to @Kusalananda in the comments)
This will unfortunately break on filenames likeFinal Thesis.pdf
(with a space).
– Kusalananda♦
May 21 at 16:17
@Kusalananda Fixed.
– user1717828
May 21 at 16:38
add a comment |
It was not specified, but here is a solution without regular expressions if anybody is interested.
We can use find . -type f
to just get files, then utilize dirname
and basename
to write the conditional. The utilities have the following behavior:
$ find . -type f
./dir2/spam/spam.pdf
./dir2/dir2.tex
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./dir1/dir1.txt
basename
returns just the filename after the last /
:
$ for file in $(find . -type f); do basename $file; done
spam.pdf
dir2.tex
dir3.pdf
eggs.pdf
dir1.pdf
dir1.txt
dirname
gives the entire path up to the final /
:
$ for file in $(find . -type f); do dirname $file; done
./dir2/spam
./dir2
./dir3
./dir3/eggs
./dir1
./dir1
Therefore, basename $(dirname $file)
gives the parent directory of the file.
$ for file in $(find . -type f); do basename $(dirname $file) ; done
spam
dir2
dir3
eggs
dir1
dir1
Solution
Combine the above to form the conditional "$(basename $file)" = "$(basename $(dirname $file))".pdf
, then only print each result from find
if that conditional returns true.
$ while read file; do if [ "$(basename "$file")" = "$(basename "$(dirname "$file")")".pdf ]; then echo $file; fi done < <(find . -type f)
./dir2/spam/spam.pdf
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./Final Thesis/grits/grits.pdf
./Final Thesis/Final Thesis.pdf
In the above example, we've added a directory/file with spaces in the name to treat that case (thanks to @Kusalananda in the comments)
This will unfortunately break on filenames likeFinal Thesis.pdf
(with a space).
– Kusalananda♦
May 21 at 16:17
@Kusalananda Fixed.
– user1717828
May 21 at 16:38
add a comment |
It was not specified, but here is a solution without regular expressions if anybody is interested.
We can use find . -type f
to just get files, then utilize dirname
and basename
to write the conditional. The utilities have the following behavior:
$ find . -type f
./dir2/spam/spam.pdf
./dir2/dir2.tex
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./dir1/dir1.txt
basename
returns just the filename after the last /
:
$ for file in $(find . -type f); do basename $file; done
spam.pdf
dir2.tex
dir3.pdf
eggs.pdf
dir1.pdf
dir1.txt
dirname
gives the entire path up to the final /
:
$ for file in $(find . -type f); do dirname $file; done
./dir2/spam
./dir2
./dir3
./dir3/eggs
./dir1
./dir1
Therefore, basename $(dirname $file)
gives the parent directory of the file.
$ for file in $(find . -type f); do basename $(dirname $file) ; done
spam
dir2
dir3
eggs
dir1
dir1
Solution
Combine the above to form the conditional "$(basename $file)" = "$(basename $(dirname $file))".pdf
, then only print each result from find
if that conditional returns true.
$ while read file; do if [ "$(basename "$file")" = "$(basename "$(dirname "$file")")".pdf ]; then echo $file; fi done < <(find . -type f)
./dir2/spam/spam.pdf
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./Final Thesis/grits/grits.pdf
./Final Thesis/Final Thesis.pdf
In the above example, we've added a directory/file with spaces in the name to treat that case (thanks to @Kusalananda in the comments)
It was not specified, but here is a solution without regular expressions if anybody is interested.
We can use find . -type f
to just get files, then utilize dirname
and basename
to write the conditional. The utilities have the following behavior:
$ find . -type f
./dir2/spam/spam.pdf
./dir2/dir2.tex
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./dir1/dir1.txt
basename
returns just the filename after the last /
:
$ for file in $(find . -type f); do basename $file; done
spam.pdf
dir2.tex
dir3.pdf
eggs.pdf
dir1.pdf
dir1.txt
dirname
gives the entire path up to the final /
:
$ for file in $(find . -type f); do dirname $file; done
./dir2/spam
./dir2
./dir3
./dir3/eggs
./dir1
./dir1
Therefore, basename $(dirname $file)
gives the parent directory of the file.
$ for file in $(find . -type f); do basename $(dirname $file) ; done
spam
dir2
dir3
eggs
dir1
dir1
Solution
Combine the above to form the conditional "$(basename $file)" = "$(basename $(dirname $file))".pdf
, then only print each result from find
if that conditional returns true.
$ while read file; do if [ "$(basename "$file")" = "$(basename "$(dirname "$file")")".pdf ]; then echo $file; fi done < <(find . -type f)
./dir2/spam/spam.pdf
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./Final Thesis/grits/grits.pdf
./Final Thesis/Final Thesis.pdf
In the above example, we've added a directory/file with spaces in the name to treat that case (thanks to @Kusalananda in the comments)
edited May 21 at 16:37
answered May 21 at 15:58
user1717828user1717828
1,75531427
1,75531427
This will unfortunately break on filenames likeFinal Thesis.pdf
(with a space).
– Kusalananda♦
May 21 at 16:17
@Kusalananda Fixed.
– user1717828
May 21 at 16:38
add a comment |
This will unfortunately break on filenames likeFinal Thesis.pdf
(with a space).
– Kusalananda♦
May 21 at 16:17
@Kusalananda Fixed.
– user1717828
May 21 at 16:38
This will unfortunately break on filenames like
Final Thesis.pdf
(with a space).– Kusalananda♦
May 21 at 16:17
This will unfortunately break on filenames like
Final Thesis.pdf
(with a space).– Kusalananda♦
May 21 at 16:17
@Kusalananda Fixed.
– user1717828
May 21 at 16:38
@Kusalananda Fixed.
– user1717828
May 21 at 16:38
add a comment |
I take bash globbing, simple loop over string tests any day over the Find program. Call me irrational, and while it may well be suboptimal such simple code does the trick for me: readable and reusable, satisfying even!. Allow me therefore to suggest a combination of:
• bash globstar : for f in ** ; do ...
** loops over every files in the current directory and all subfolders.. to check globstar status in your current session: shopt -p globstar
. To activate globstar: shopt -s globstar
.
• "file" utlity : if [[ $(file "$f") =~ pdf ]]; then ...
to check actual file format for pdf - more robust than testing only for the file's extension
• basename, dirname :
to compare the file name to the name of the directory immediately above it. basename
returns the filename - dirname
returns entire directory path - combine the two functions to only return the one directory containing the matching file.
I put each one in a variable (_mydir and _myf) to then do a simple test using =~ for string matching.
One subtility: remove any "dot" in the filename to avoid matching filename to current directory whose shortcut is also "." - I used direct string substitution on the variable _myf : $_myf//./
- not very elegant but it works. Positive matches will return each file's path - together with the full path of the current folder by preceding the output with : $(pwd)/
.
Code
for f in ** ; do
if [[ $(file "$f") =~ PDF ]]; then
_mydir="$(basename $(dirname $f))" ;
_myf="$(basename $f)" ;
[[ "$_myf//./" =~ "$_mydir" ]] && echo -e "$(pwd)/$f" ;
fi ;
done
add a comment |
I take bash globbing, simple loop over string tests any day over the Find program. Call me irrational, and while it may well be suboptimal such simple code does the trick for me: readable and reusable, satisfying even!. Allow me therefore to suggest a combination of:
• bash globstar : for f in ** ; do ...
** loops over every files in the current directory and all subfolders.. to check globstar status in your current session: shopt -p globstar
. To activate globstar: shopt -s globstar
.
• "file" utlity : if [[ $(file "$f") =~ pdf ]]; then ...
to check actual file format for pdf - more robust than testing only for the file's extension
• basename, dirname :
to compare the file name to the name of the directory immediately above it. basename
returns the filename - dirname
returns entire directory path - combine the two functions to only return the one directory containing the matching file.
I put each one in a variable (_mydir and _myf) to then do a simple test using =~ for string matching.
One subtility: remove any "dot" in the filename to avoid matching filename to current directory whose shortcut is also "." - I used direct string substitution on the variable _myf : $_myf//./
- not very elegant but it works. Positive matches will return each file's path - together with the full path of the current folder by preceding the output with : $(pwd)/
.
Code
for f in ** ; do
if [[ $(file "$f") =~ PDF ]]; then
_mydir="$(basename $(dirname $f))" ;
_myf="$(basename $f)" ;
[[ "$_myf//./" =~ "$_mydir" ]] && echo -e "$(pwd)/$f" ;
fi ;
done
add a comment |
I take bash globbing, simple loop over string tests any day over the Find program. Call me irrational, and while it may well be suboptimal such simple code does the trick for me: readable and reusable, satisfying even!. Allow me therefore to suggest a combination of:
• bash globstar : for f in ** ; do ...
** loops over every files in the current directory and all subfolders.. to check globstar status in your current session: shopt -p globstar
. To activate globstar: shopt -s globstar
.
• "file" utlity : if [[ $(file "$f") =~ pdf ]]; then ...
to check actual file format for pdf - more robust than testing only for the file's extension
• basename, dirname :
to compare the file name to the name of the directory immediately above it. basename
returns the filename - dirname
returns entire directory path - combine the two functions to only return the one directory containing the matching file.
I put each one in a variable (_mydir and _myf) to then do a simple test using =~ for string matching.
One subtility: remove any "dot" in the filename to avoid matching filename to current directory whose shortcut is also "." - I used direct string substitution on the variable _myf : $_myf//./
- not very elegant but it works. Positive matches will return each file's path - together with the full path of the current folder by preceding the output with : $(pwd)/
.
Code
for f in ** ; do
if [[ $(file "$f") =~ PDF ]]; then
_mydir="$(basename $(dirname $f))" ;
_myf="$(basename $f)" ;
[[ "$_myf//./" =~ "$_mydir" ]] && echo -e "$(pwd)/$f" ;
fi ;
done
I take bash globbing, simple loop over string tests any day over the Find program. Call me irrational, and while it may well be suboptimal such simple code does the trick for me: readable and reusable, satisfying even!. Allow me therefore to suggest a combination of:
• bash globstar : for f in ** ; do ...
** loops over every files in the current directory and all subfolders.. to check globstar status in your current session: shopt -p globstar
. To activate globstar: shopt -s globstar
.
• "file" utlity : if [[ $(file "$f") =~ pdf ]]; then ...
to check actual file format for pdf - more robust than testing only for the file's extension
• basename, dirname :
to compare the file name to the name of the directory immediately above it. basename
returns the filename - dirname
returns entire directory path - combine the two functions to only return the one directory containing the matching file.
I put each one in a variable (_mydir and _myf) to then do a simple test using =~ for string matching.
One subtility: remove any "dot" in the filename to avoid matching filename to current directory whose shortcut is also "." - I used direct string substitution on the variable _myf : $_myf//./
- not very elegant but it works. Positive matches will return each file's path - together with the full path of the current folder by preceding the output with : $(pwd)/
.
Code
for f in ** ; do
if [[ $(file "$f") =~ PDF ]]; then
_mydir="$(basename $(dirname $f))" ;
_myf="$(basename $f)" ;
[[ "$_myf//./" =~ "$_mydir" ]] && echo -e "$(pwd)/$f" ;
fi ;
done
answered May 24 at 2:34
docgyneco69docgyneco69
6113
6113
add a comment |
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f520078%2ffinding-all-files-with-a-given-extension-whose-base-name-is-the-name-of-the-pare%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
sLXfS2Nagp6DVDMPTdzETGjZ5IVxoEZlt2 Z gkQh1DshBZSfk8Lxy3Vzue9Qq6KKOgbnjNpXfPwAp2HEfl1eIx3Gn5VaiA
Yes, I'll mock up an example now.
– Brian Fitzpatrick
May 21 at 5:10
1
@Inian Added an example. Does this help?
– Brian Fitzpatrick
May 21 at 5:15