Matt Parkins
Matt Parkins

Reputation: 24708

Strip path from find for use in pipe

I'm trying to use "find" to execute a command on a bunch of HTML files and then pipe out the result to a different directory. However, the pipe is failing because the filename found by "find" includes the path, and the path does not fit the directory structure of the pipe target. Essentially I need to strip the path from the second instance of {} below.

find subd/*.html -type f -exec "./mycmd {} opts > subd2/{}" \;

This is a simplified version of the command, but in the real world, putting a "../" before mysubdirectory2 will not work, nor does prefixing "cd subd && ".

Upvotes: 2

Views: 515

Answers (5)

Matt Parkins
Matt Parkins

Reputation: 24708

Ok, I found the solution in the end, which was to use single quotes rather than double quotes, and use the $() evaluation form for basename:

find subd/*.html -type f -exec './mycmd {} opts > subd2/$(basename {})' \;

I have no idea why single quotes are the difference, or any idea why Andy's solution did not work on my machine.

Upvotes: 0

Josh Cartwright
Josh Cartwright

Reputation: 801

Similar to Jonathan's solution, but remove the unnecessary extra script.

find subd -name '*.html' -type f -exec sh -c '
    for f; do
        ./mycmd "$f" opts > subd2/"${f##*/}"
    done
' _ {} '+'

Upvotes: 2

andy magoon
andy magoon

Reputation: 2929

find subd/*.html -type f -exec "./mycmd {} opts > subd2/\`basename {}\`" \;

Update: Added escapes before backticks surrounding basename

Upvotes: 1

twalberg
twalberg

Reputation: 62469

Something like this ought to work:

while read fn
do
  ./mycmd "${fn}" opts > "subd2/${fn##*/}"
done < <(find subd -type f -name "*.html")

Upvotes: 1

Jonathan Leffler
Jonathan Leffler

Reputation: 754710

Time to write a script (call it mycmd-invoker) that does the 'correct job' and run that from find:

find subd/*.html -type f -exec mycmd-invoker {} +

The script gets given a list of file names with path and can deal with the issues:

for file in "$@"
do
    base=$(basename "$file")
    ./mycmd "$file" opts > subd2/"$base" 
done

You can throw the mycmd-invoker script away once you're done with the task, unless you think you might need to do it again.

Upvotes: 1

Related Questions