3

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.

  • 1
    Please explain what you want to achieve, perhaps with a directory tree before and after. The phrase "Only, this does not result in empty files a and b in the parent directory." appears to suggest you want to create files while your opening statement suggests the files already exist. – Bram May 30 '12 at 7:32
  • @Bram I had the impression it follow a bit from my example find command, but I added it to the question. – Bernhard May 30 '12 at 7:35
1

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) [ :-) ] janmoesen@mail ~/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) [ :-) ] janmoesen@mail ~/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) [ :-) ] janmoesen@mail ~/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
4

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)

3

Try this at your own risk:

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

Tested with filenames with spaces.

1

How about this?

$ touch $(find test -type f -exec basename {} \;)
  • Suppose that my touch command only takes one argument at a time? – Bernhard May 30 '12 at 7:06
  • 3
    Will break on filenames with whitespace. – Chris Down May 30 '12 at 9:11
1

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 -- "$@"' "$PWD" {} +

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

touch test/**/*(:t)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.