Reputation: 46919
I have the following commands. Wherever the .user.log
file is present, we need to print the parent directories (i.e hht
and wee1
.) How can this be done?
$ cd /nfs//office/ && find . -name '.user.log'
./hht/info/.user.log
./wee1/info/.user.log
Upvotes: 19
Views: 28590
Reputation: 227
Here’s a one-liner using awk
. This is useful if you don’t have access to printf
(eg. on macOS) or wish to have greater and more easily parsable control over which parts of the path you wish to extract.
find . -iregex ".*.user.log" | awk 'BEGIN{FS="/"; OFS="/"} {$NF=""; print $0}' | uniq
find
command with a proper regex will already find what you’re looking for.awk
then creates fields for each find
result using /
as field separator FS
(and replacing them afterwards with OFS
) and prints the entire line (print $0
) except for the final field ($NF=“"
) which is the filename you may not want to see (eg. .user.log
).uniq
simply ensures you don’t get multiple copies of the same path if several copies of the file pattern appear in a single directory.If you don’t want the entire paths but only parts thereof, change print $0
to print $4
for example.
Upvotes: 0
Reputation: 1
for i in $(find . -name ".user.log" -exec readlink -f {} \;)
do
parent=$(dirname $i)
echo $parent
done
this may work and produces output like
/nfs/office/hht/info
/nfs/office/wee1/info
Upvotes: 0
Reputation: 1297
You can do this easily with the formatting options of the -printf
action of find
(see man find
).
cd /nfs//office/ && find . -name '.user.log' -printf "%h\n"
./hht/info
./wee1/info
From the man page:
%h\n
will print path for each file on a new line.
Please note that -printf
is GNU-only. It won't work on macOS (a BSD system).
Upvotes: 13
Reputation: 36703
Am I missing something here. Surely all this regex and/or looping is not necessary, a one-liner will do the job. Also "for foo in $()" solutions will fail when there are spaces in the path names.
Just use dirname twice with xargs, to get parent's parent...
# make test case
mkdir -p /nfs/office/hht/info
mkdir -p /nfs/office/wee1/info
touch /nfs/office/hht/info/.user.log
touch /nfs/office/wee1/info/.user.log
# parent's parent approach
cd /nfs//office/ && find . -name '.user.log' | xargs -I{} dirname {} | xargs -I{} dirname {}
# alternative, have find print parent directory, so dirname only needed once...
cd /nfs//office/ && find . -name ".user.log" -printf "%h\n" | xargs -I{} dirname {}
Produces
./hht
./wee1
Upvotes: 20
Reputation: 58798
@trojanfoe has the right idea; this is just a way to get it to work safely with any filename, and pretty much any command within the loop:
while IFS= read -r -d '' -u 9
do
echo "$(dirname -- "$(dirname -- "$REPLY")")"
done 9< <( find "/nfs/office/" -name '.user.log' -print0 )
If you want it to echo only the unique names:
while IFS= read -r -d '' -u 9
do
echo "$(dirname -- "$(dirname -- "$REPLY")")"
done 9< <( find "/nfs/office/" -name '.user.log' -print0 ) | sort -u
Upvotes: 1
Reputation: 40374
You could do something like this:
cd /nfs/office/ && find . -name '.user.log' | xargs -n1 -I{} expr match {} '\(\.\(\/[^/]*\/\)\?\)'
where the xargs
uses expr match
to extract the part that starts with .
until the first match of directory between slash characters (/dir/
).
An alternative version using sed
would be as follows:
cd /nfs/office/ && find . -name 'file.txt' | sed -r 's|(\./([^/]*/)?).*|\1|'
Upvotes: 0
Reputation: 2459
find /nfs/office -name '.user.log' | while read line
do
echo $line | awk -F/ '{print $(NF-1),$NF}'
done
Upvotes: -1
Reputation: 122391
for file in $(find /nfs/office -name .user.log -print)
do
parent=$(dirname $(dirname $file))
echo $parent
done
EDIT: Sorry missed that you want the grandparent directory.
Upvotes: 3