What does % do in Linux shell strings?

In Linux shell, what does % do, as in:

for file in *.png.jpg; do
  mv "$file" "${file%.png.jpg}.jpg"
done

Solution 1:

When % is used in pattern ${variable%substring} it will return content of variable with the shortest occurance of substring deleted from back of variable.

This function supports wildcard patterns - that's why it accepts star (asterisk) as a substite for zero or more characters.

It should be mentioned that this is Bash specific - other linux shells don't neccessarily contain this function.

If you want to learn more about string manipulation in Bash, i highly suggest reading this page. Among other handy functions it - for example - explains what does %% do :)

Edit: I forgot to mention that when it's used in pattern $((variable%number)) or $((variable1%$variable2)) the % character will function as modulo operator. DavidPostill has more specific documentation links in his answer.

When % is used in different context, it should be recognized as regular character only.

Solution 2:

Bash Reference Manual: Shell Parameter Expansion

${parameter%word}
${parameter%%word}

The word is expanded to produce a pattern just as in filename expansion. If the pattern matches a trailing portion of the expanded value of parameter, then the result of the expansion is the value of parameter with the shortest matching pattern (the ‘%’ case) or the longest matching pattern (the ‘%%’ case) deleted. If parameter is ‘@’ or ‘*’, the pattern removal operation is applied to each positional parameter in turn, and the expansion is the resultant list. If parameter is an array variable subscripted with ‘@’ or ‘*’, the pattern removal operation is applied to each member of the array in turn, and the expansion is the resultant list.

Solution 3:

By experimenting, I find that a match after % is discarded, when the string is enclosed in curly brackets (braces).

To illustrate:

touch abcd         # Create file abcd

for file in ab*; do
 echo $file        # echoes the filename
 echo $file%       # echoes the filename plus "%"
 echo ${file%}     # echoes the filename
 echo "${file%}"   # echoes the filename
 echo
 echo "${file%c*}" # Discard anything after % matching c*
 echo "${file%*}"  # * is not greedy
 echo ${file%c*}   # Without quotes works too
 echo "${file%c}"  # No match after %, no effect
 echo $file%c*     # Without {} fails
done

Here is the output:

abcd
abcd%
abcd
abcd

ab
abcd
ab
abcd
abcd%c*

Solution 4:

In Linux shell (bash), what does % do?

for file in *.png.jpg; do
  mv "$file" "${file%.png.jpg}.jpg"
done

In this particular case, the % is pattern matching operator (note it can also be a modulo operator).


Pattern Matching Operator

${var%$Pattern}, ${var%%$Pattern}

${var%$Pattern} Remove from $var the shortest part of $Pattern that matches the back end of $var.

${var%%$Pattern} Remove from $var the longest part of $Pattern that matches the back end of $var.

Example: Pattern matching in parameter substitution

#!/bin/bash
# patt-matching.sh

# Pattern matching  using the # ## % %% parameter substitution operators.

var1=abcd12345abc6789
pattern1=a*c  # * (wild card) matches everything between a - c.

echo
echo "var1 = $var1"           # abcd12345abc6789
echo "var1 = ${var1}"         # abcd12345abc6789
                              # (alternate form)
echo "Number of characters in ${var1} = ${#var1}"
echo

echo "pattern1 = $pattern1"   # a*c  (everything between 'a' and 'c')
echo "--------------"
echo '${var1#$pattern1}  =' "${var1#$pattern1}"    #         d12345abc6789
# Shortest possible match, strips out first 3 characters  abcd12345abc6789
#                                     ^^^^^               |-|
echo '${var1##$pattern1} =' "${var1##$pattern1}"   #                  6789      
# Longest possible match, strips out first 12 characters  abcd12345abc6789
#                                    ^^^^^                |----------|

echo; echo; echo

pattern2=b*9            # everything between 'b' and '9'
echo "var1 = $var1"     # Still  abcd12345abc6789
echo
echo "pattern2 = $pattern2"
echo "--------------"
echo '${var1%pattern2}  =' "${var1%$pattern2}"     #     abcd12345a
# Shortest possible match, strips out last 6 characters  abcd12345abc6789
#                                     ^^^^                         |----|
echo '${var1%%pattern2} =' "${var1%%$pattern2}"    #     a
# Longest possible match, strips out last 12 characters  abcd12345abc6789
#                                    ^^^^                 |-------------|

# Remember, # and ## work from the left end (beginning) of string,
#           % and %% work from the right end.

echo

exit 0

Source Parameter Substitution


Modulo Operator

%

modulo, or mod (returns the remainder of an integer division operation)

bash$ expr 5 % 3
2

5/3 = 1, with remainder 2

Source Operators