2

I'm trying to use find inside a loop to create a variable that contains a file matching the filename + desired string

Example:

file1.en.srt file1.mkv file1.pt.srt 

This is the relevant part of the code:

shopt -s nullglob shopt -s nocaseglob if [ -d "$1" ]; then for file in "${1%/}/"*mkv; do # Get filename to match against subs and audios filename="$(basename "$file" .mkv)" # Find matching subtitle file engsubs="$(find . -name "$filename*en.srt*" | sed -e 's,^\./,,')" # Find matching audio file engaudio="$(find . -iname "$filename*en.ac3" -o -iname "$filename*en.eac3" -o -iname "$filename*en.dts" | sed -e 's,^\./,,')" done fi 

It works if files don't contain brackets, but the find commands don't find anything for files whose names contain brackets. Why this is happening? I want to create a variable like $en that would contain file1.en.srt

6
  • 6
    Please don't just tell us "it works" and "it doesn't work". How does it fail? What error message do you get? Which part of it fails? How do you call this script? What is $1? Commented Mar 25, 2019 at 9:18
  • No error message, it just won't find anything if the filenames contains brackets []... I call this script from command line... $1 would be the path Commented Mar 25, 2019 at 9:22
  • 2
    please try to create a minimal code example for us to reproduce without your whole script. Commented Mar 25, 2019 at 9:24
  • 2
    Related: Why is looping over find's output bad practice? Commented Mar 25, 2019 at 9:32
  • Also related: Why does my shell script choke on whitespace or other special characters? Commented Mar 25, 2019 at 9:35

1 Answer 1

10

The problem is that [ and ] are glob characters. For example, consider this file:

ba[r].mkv 

When running your script on that file, $filename will be: ba[r] and, therefore, your find command will be:

find . -name 'ba[r]*pt-BR.srt*' 

Since [r] is a single-letter character class, it means r. So your command is looking for a filename starting with ba, then an r, then any character(s), and pt-BR.srt and any characters again. You need to escape the brackets:

find . -name 'ba\[r\]*pt-BR.srt*' 

The simplest way is to use printf and %q. So change this line:

filename="$(basename "$file" .mkv)" 

To this:

filename=$(printf '%q' "$(basename "$file" .mkv)") 

Or, without the command substitution around printf:

printf -v filename '%q' "$(basename "$file" .mkv)" 
3
  • Yeah I just noticed now that I didn't need that * at the end of srt or other file extensions lol. And it worked...thanks so much <3. Do you think this could mess with other filenames containing space, (), ç or other special characters? Commented Mar 25, 2019 at 9:42
  • 1
    @Freedo no, the spaces and ( will also be escaped. Try running printf '%q\n' "a ho[r]i(b)le file". Commented Mar 25, 2019 at 9:51
  • printf %q is wrong in the general case. First the output varies greatly between implementations but even if we limit to the printf builtin of bash, it still uses different kinds of quoting for different characters. For instance, it prints non-printable characters or non-characters using the $'...' form of quotes from ksh93. Here you just need to add a backslash in front of backslash, *?[]. Or use zsh and its b parameter expansion flag. Commented Nov 29, 2022 at 8:10

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.