0

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.

3
  • See mywiki.wooledge.org/UsingFind Commented May 9, 2017 at 15:29
  • 1
    And run your code through shellcheck.net -- it's missing quotes, which is your largest immediate problem, not at all specific to find. Commented May 9, 2017 at 15:30
  • That said, if you're editing a JSON file, you should really use jq rather than hackery with sed. You're going to have a bad time here if any names need to be escaped or quoted to be valid inside your HTML file. Commented May 9, 2017 at 15:33

2 Answers 2

4

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.
Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.