Errors in execution of shell script which consists of command substitution

Hi I am Rupesh from India and I have some MP4 video files and I want to compress these files using ffmpeg command and shell scripting.

Hi ffprobe tool can be used to extract the bitrate of the source input mp4 video file which later can be used in ffmpeg command as below.

ffprobe -v error -select_streams v:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1 input.mp4


ffmpeg -y -i  -c:v libx265 -b:v bitrate -preset medium -c:a libfdk_aac -b:a 52k -ar 44100 input_compressed.mp4;

Actually the above process is working with one file I mean I have used ffprobe tool to get the video bitrate and after that I have used it by invoking ffmpeg command by providing the bitrate value obtained from ffprobe tool.

I want to apply the same process in a Linux shell script and run it and so I have developed a small shell script as below.

for i in *.mp4; 


 do name=`echo $i | cut -d'.' -f1`; 

 echo $name; 


 $temp=`ffprobe -v error -select_streams v:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1 $i`;

 $br=`echo $temp | cut -d'=' -f1`; 


ffmpeg -y -i "$i" -cpucount 3 -c:v libx265 -b:v $br -preset medium -c:a libfdk_aac -b:a 52k -ar 44100 "${name}_compressed.mp4"; 

done

Upon running the above script I am getting the following errors.

syntax error near unexpected token `$temp=`ffprobe -v error -select_streams v:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1 $i`'

./ffmpeg_Linux_script_with variable.sh: line 4: =: command not found

./ffmpeg_Linux_script_with variable.sh: line 5: =: command not found

[NULL @ 0x30ab980] Unable to find a suitable output format for 'medium'

Can you suggest what's wrong with the above script.

Don't expand variables that are an assignment target. Use like

temp=...
1 Like

Perhaps you have been "spoiled" by perl where a scalar variable is always $variable even in assignments. And only in perl you must end a command with a semicolon.

Put "quotes" around a $var in command arguments!

temp=`ffprobe -v error -select_streams v:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1 "$i"`
br=`echo "$temp" | cut -d'=' -f1`

More efficient than echo and cut commands are the shell's variable modifiers:

br=${temp%%=*}

This deletes from the right end to a = ; see
man bash

This is an assignment not a command. You can use "quotes" nevertheless.
You really should use quotes in command arguments:

command "$br"
command "${temp%%=*}"
2 Likes

@rupeshforu3, in addition to all the advice given by other team members, a good habit to form is verifying the integrity of shell scripts by running them through the shellcheck utility (if not installed ask your system admin to install or ask google how to install shellcheck )

for your script (named test.sh for convenience), running it through shellcheck produced following:

shellcheck test.sh

In test.sh line 1:
for i in *.mp4; 
^-- SC2148: Tips depend on target shell and yours is unknown. Add a shebang.


In test.sh line 4:
 do name=`echo $i | cut -d'.' -f1`; 
         ^-- SC2006: Use $(..) instead of legacy `..`.
               ^-- SC2086: Double quote to prevent globbing and word splitting.


In test.sh line 6:
 echo $name; 
      ^-- SC2086: Double quote to prevent globbing and word splitting.


In test.sh line 9:
 $temp=`ffprobe -v error -select_streams v:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1 $i`;
  ^-- SC1066: Don't use $ on the left side of assignments.
       ^-- SC2006: Use $(..) instead of legacy `..`.
                                                                                                          ^-- SC2086: Double quote to prevent globbing and word splitting.


In test.sh line 11:
 $br=`echo $temp | cut -d'=' -f1`; 
  ^-- SC1066: Don't use $ on the left side of assignments.
     ^-- SC2006: Use $(..) instead of legacy `..`.
           ^-- SC2086: Double quote to prevent globbing and word splitting.


In test.sh line 14:
ffmpeg -y -i "$i" -cpucount 3 -c:v libx265 -b:v $br -preset medium -c:a libfdk_aac -b:a 52k -ar 44100 "${name}_compressed.mp4"; 
                                                ^-- SC2086: Double quote to prevent globbing and word splitting.
2 Likes

Our friends here already pointed out all important stuff. I'll add two less important details which help on script's readability:

  1. Pls don't insert line breaks between all lines of code. This just makes your script twice as long. Use newlines to split logic blocks, so the logic gets clearer.
  2. Linux shell don't use colons as line endings. They don't generate errors, but makes code uglier.

Here is all previous excellent replays all together.
It's important to tell on the 1st line which interpreter you need to execute the script.
Which sh or awk or perl or ...
If like to put more than one command / line, then you need ; between commands when using *sh

#!/usr/bin/bash  
# or /usr/bin/ksh or /usr/bin/dash or ...
# don't use /usr/bin/sh - why not ? You never know which shell has set the default "sh" - 
#  system sh is usually Bourne shell compatible, but also posix-sh compatible ?  Maybe.
# I'll say that select which shell interpreter you are using in your script and tell it: bash, ksh, dash, zsh, ... 
# 
# set variable value, no spaces between variablename, = and value.  1st element on the command line
#    var=value
# using variable value - expand value of variable = add $ before variablename
#    $var
# Reading ref. manual you'll get the exact definition.
#

for i in *.mp4
do
  #name=$(echo $i | cut -d'.' -f1)"
  name=${i%%.*}  # remove all after 1st dot => 1st "fld"    # builtin, no need to use external command like cut, awk, ...
  echo "$name"

  temp=$(ffprobe -v error -select_streams v:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1 "$i" )
  #br=`echo $temp | cut -d'=' -f1`
  br=${temp%%=*}
  ffmpeg -y -i "$i" -cpucount 3 -c:v libx265 -b:v "$br" -preset medium -c:a libfdk_aac -b:a 52k -ar 44100 "${name}_compressed.mp4"
done

Parsing string using some delimiter, generic solution is to use array. Builtin. No need to use external cut, awk, ...

i="xxx.yyy.zzz.aaa"
saveifs="$IFS"  # save current IFS 
# use IFS to parse string to the array flds using dot
IFS="."            # set delimiter
flds=($i)         # parse to the array flds
IFS="$saveifs"    # return IFS
echo "1st:${flds[0]}"
# one line - 3 commands - not so easy to read
saveifs="$IFS";  IFS="." flds=($i)  ;  IFS="$saveifs"

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.