0

I have the following code snippet in a script:

bitrate=$(ffmpeg -i "$vidfile" 2>&1 | grep bitrate | \
              perl -pe 's/.*bitrate: (\d{1,} .*)\r?\n\r?$/$1/')
echo "\$bitrate = $bitrate" #added for debugging
printf "\$bitrate = $bitrate\n" #added for debugging
printf " $bitrate $vidfile\n" #added for debugging
printf " %s %s \n\n" "$bitrate" "$vidfile"

This BASH shell script (minus the lines added for debugging) was working fine as-is on Mac OS X Terminal. When when I try to run it on Cygwin, the last & 2nd-last printf don't print the value of the $bitrate variable.

Sample output from a run:

$ bitrate.sh 20180305-01.mov 20180305-02.mov
$bitrate = 31103 kb/s
$bitrate = 31103 kb/s
 20180305-01.mov
 20180305-01.mov

$bitrate = 30735 kb/s
$bitrate = 30735 kb/s
 20180305-02.mov
 20180305-02.mov
markvgti
  • 4,321
  • 7
  • 40
  • 62
  • 2
    Te Cygwin version of your script probably has DOS/Windows line endings, and so is embedding carriage returns in the variables. Try printing the variables using printf's `%q` format instead of `%s`, and see if they end with `\r`. See ["Are shell scripts sensitive to encoding and line endings?"](https://stackoverflow.com/questions/39527571/are-shell-scripts-sensitive-to-encoding-and-line-endings). – Gordon Davisson Mar 11 '18 at 06:25
  • 1
    You were correct. I added `tr -d '\r'` to the end of the line calculating `bitrate` & it started working. The script file itself is in Unix format. Thanks! – markvgti Mar 11 '18 at 07:24
  • @GordonDavisson Could you add this as an answer so that I can mark it as the accepted one. Thanks. – markvgti Mar 12 '18 at 05:17

1 Answers1

1

The problem is due to the nonprinting carriage return character (sometimes represented as \r or ^M or <CR>). DOS and Windows use the convention that lines of text are terminated with a carriage return followed by a linefeed, but unix just uses a linefeed. As a result, when you take a DOS/Windows-format line and hand it to a unix program (e.g. the shell), it thinks the linefeed is the end of the line, and treats the carriage return as part of the content of the line. This problem happens a lot when using unix tools in Windows (e.g. Cygwin).

In this case, I assumed it was because the script file itself was in DOS/Windows format, but apparently not; so I'm not sure exactly where the carriage return is coming from (perhaps perl is trying to be Windows-compatible and outputting DOS/Windows format?). Anyway, the net result is that the variable bitrate has a carriage return at the end. Printing it by itself, this isn't visible, but if you print it followed by a space and the filename, it sends "31103 kb/s\r 20180305-01.mov" to the terminal, which prints "31103 kb/s", then the carriage return makes it go back to the beginning of the line (that's actually what "carriage return" means"), then it prints " 20180305-01.mov" over top of the "31103 kb/s", replacing it.

Solution: remove the carriage return, by adding tr -d '\r' to the end of the pipeline calculating the bitrate.

Useful troubleshooting tricks when you suspect a problem like this: print variables with printf's %q format:

$ printf 'the var is %q\n' "$bitratenormal"
the var is 31103\ kb/s
$ printf 'the var is %q\n' "$bitratewithcr"
the var is $'31103 kb/s\r'

...and run files through LC_ALL=C cat -vet to make a variety of normally-invisible characters visible:

$ cat filewithweirdchars 
This line is normal
This line ends with a space 
This line ends with a tab   
This line ends with a carriage return
This line has nonbreaking spaces and “weird” unicode quotes
$ LC_ALL=C cat -vet filewithweirdchars 
This line is normal$
This line ends with a space $
This line ends with a tab^I$
This line ends with a carriage return^M$
ThisM-BM- lineM-BM- hasM-BM- nonbreakingM-BM- spaces and M-bM-^@M-^\weirdM-bM-^@M-^] unicode quotes$
Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151