Execute on the basename of a find command

Suppose I have a directory structure as follows

test
test/a
test/b

Now I want to execute a command, such that in the . folder I can execute a command on the basename of the files a and b.

So basically, I want something like this, which I naively tried

find test -type f -exec touch `basename {}` ;

Only, this does not result in empty files a and b in the parent directory. Suppose that my touch command can only take a single argument.

This will result in a directory structure like this

a
b
test
test/a
test/b

I know how to do this in a bash script, but I am interested in single command solution.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Execute bash.

find ... -exec bash -c 'touch "${1##*/}"' btouch {} ;

Here ${1##*/} means: remove the longest match from the second argument ending with a /, thus keeping everything after the last /.

btouch can be any dummy name, to make it appear properly in the active processes list (ps)

Method 2

Try this at your own risk:

find test -type f -exec echo touch "$(basename "'{}'")" ; | bash

Tested with filenames with spaces.

Method 3

Try the execdir option for find: it executes the command you specify in the directory of the file, using only its basename as the argument

From what I gather, you want to create “a” and “b” in the “main” directory. We can do that by combining $PWD and the -execdir option. Have a look at the solution below. (The && find … ls parts are for output only, so you can see the effects. You’ll want to use the command before the &&.)

First, I set up the testing environment:

-----[ 15:40:17 ] (!6293) [ :-) ] <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="eb818a8586848e988e85ab868a8287">[email protected]</a> ~/stack 
$ mkdir test && touch test/{a,b} && find . -exec ls -dalF {} +
drwxr-xr-x 3 janmoesen janmoesen 4096 2012-05-31 15:40 ./
drwxr-xr-x 2 janmoesen janmoesen 4096 2012-05-31 15:40 ./test/
-rw-r--r-- 1 janmoesen janmoesen    0 2012-05-31 15:40 ./test/a
-rw-r--r-- 1 janmoesen janmoesen    0 2012-05-31 15:40 ./test/b

This is what happens when you use a simple -exec — the original files are touched:

-----[ 15:40:30 ] (!6294) [ :-) ] <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="8fe5eee1e2e0eafceae1cfe2eee6e3">[email protected]</a> ~/stack 
$ find test -type f -exec touch {} ; && find . -exec ls -dalF {} +
drwxr-xr-x 3 janmoesen janmoesen 4096 2012-05-31 15:40 ./
drwxr-xr-x 2 janmoesen janmoesen 4096 2012-05-31 15:40 ./test/
-rw-r--r-- 1 janmoesen janmoesen    0 2012-05-31 15:40 ./test/a
-rw-r--r-- 1 janmoesen janmoesen    0 2012-05-31 15:40 ./test/b

However, if we combine $PWD with the argument placeholder {} and use -execdir, we achieve what (I think) you want:

-----[ 15:40:57 ] (!6295) [ :-) ] <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="83e9e2edeeece6f0e6edc3eee2eaef">[email protected]</a> ~/stack 
$ find test -type f -execdir touch "$PWD/{}" ; && find . -exec ls -dalF {} +
drwxr-xr-x 3 janmoesen janmoesen 4096 2012-05-31 15:41 ./
-rw-r--r-- 1 janmoesen janmoesen    0 2012-05-31 15:41 ./a
-rw-r--r-- 1 janmoesen janmoesen    0 2012-05-31 15:41 ./b
drwxr-xr-x 2 janmoesen janmoesen 4096 2012-05-31 15:40 ./test/
-rw-r--r-- 1 janmoesen janmoesen    0 2012-05-31 15:40 ./test/a
-rw-r--r-- 1 janmoesen janmoesen    0 2012-05-31 15:40 ./test/b

Method 4

How about this?

$ touch $(find test -type f -exec basename {} ;)

Method 5

Invoke a shell to do a bit of string processing on the path. You can use the ${VARIABLE##PATTERN} string manipulation construct to strip off a prefix matching PATTERN from VARIABLE — the pattern */ strips off the leading directory names.

find test -type f -exec sh -c 'for x; do touch "${x##*/}"; done' _ {} +

An alternate method with GNU find is to have it change into the subdirectories. Invoke a shell to change back to the original directory.

find test -type f -execdir sh -c 'cd "$0" && touch -- "<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1d395d">[email protected]</a>"' "$PWD" {} +

Or drop find and use zsh. Specifically, add the t history modifier as a glob qualifier.

touch test/**/*(:t)


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x