4

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.

Bernhard
  • 11,992
  • 4
  • 59
  • 69
  • 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 07: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 07:35

5 Answers5

5

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)

Bernhard
  • 11,992
  • 4
  • 59
  • 69
Ignacio Vazquez-Abrams
  • 44,857
  • 7
  • 93
  • 100
3

Try this at your own risk:

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

Tested with filenames with spaces.

AndresVia
  • 228
  • 1
  • 5
2

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
janmoesen
  • 2,680
  • 17
  • 16
1

How about this?

$ touch $(find test -type f -exec basename {} \;)
Abhishek A
  • 222
  • 1
  • 3
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)
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175