Yves
Yves

Reputation: 12371

How to use find and exec to execute multiple commands

I have such a need:
In the directory video, I need to

find ./video -type f | while read myfile; do
    tmp=`basename $myfile`   #example.mp4
    tmp="${tmp/.mp4/.html}"  #example.html
    cp index.html "$tmp"
    sed -i '' "s#sceneFilePath:.*#sceneFilePath: \"$myfile\",#g" $tmp
#done;

Here is my directory:

dir
 |--- video
 |     |--- example.mp4
 |--- index.html
 |--- generateHtml.sh

generateHtml.sh is just like above.
Here is what it does:
find the example.mp4 file in video, then cp index.html example.html and change a string in the example.html.

It works well.

But now for some path and name of .mp4 files, there are some special characters, such as &, , -. It doesn't seem that while read works for these cases.

I've heard that find -exec can handle all of special characters but I don't know how to use it for this case.

Upvotes: 0

Views: 2046

Answers (2)

chepner
chepner

Reputation: 531325

If you are using bash 4 or later, I wouldn't bother with find here; a for loop will be much simpler. Note there is no need to copy the file, then edit it in-place; just redirect the output of the sed command to the desired file.

for f in video/**/*.mp4; do
  [ -f "$f" ] || continue
  tmp=$(basename "$f" .mp4).html
  sed "s#sceneFilePath:.*#sceneFilePath: \"$f\",#g" index.html >  "$tmp"
done

If you don't actually need to recurse into subdirectories of video, you can simply use for f in video/*.mp4; do, and the whole thing works not just in earlier versions of bash, but environment where the shell and sed are both POSIX-compliant.

Upvotes: 1

Charles Duffy
Charles Duffy

Reputation: 295480

See Using Find for detailed discussion.

find ./video -type f -print0 | while IFS= read -r -d '' myfile; do
    tmp=$(basename "$myfile")   #example.mp4  -- consider also tmp=${myfile##*/}
    tmp="${tmp%.mp4}.html"      #example.html
    sed "s#sceneFilePath:.*#sceneFilePath: \"$myfile\",#g" \
      <index.html >"$tmp"
done

Note:

  • -print0 is used on the find end, and IFS= read -r -d '' is used on the read end; this ensures that all possible filenames (including names with newlines, including names with leading or trailing whitespace) are supported.
  • The former substitution, which replaced the first instance of .mp4 anywhere in the filename with .html, has been replaced with one which strips .mp4 off the end of the filename, and appends .html.
  • "$myfile" is quoted in invoking basename. This was your most substantial immediate bug in the original code, as previously a filename could be split into multiple separate arguments to basename.
  • $() is used instead of backticks. This modern (and yes, POSIX-compliant) command substitution syntax can be easily nested and has much clearer semantics for backslash escapes within.
  • sed -i, which is nonstandard and nonportable (the above was valid for MacOS but not for GNU), is unneeded here; one can skip the cp and do the transform in-line.

Upvotes: 4

Related Questions