32

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

for file in *.png.jpg; do
  mv "$file" "${file%.png.jpg}.jpg"
done
4
  • 4
    Every time I look this up, I usually find the right section of the bash man page by searching for %% or ##, since that's memorable and rare enough to find the right section quickly. man bash /## Aug 31, 2016 at 5:57
  • 3
    @PeterCordes I always rememer this by "It's 5%, so % is at the end and thus cuts from the end. And it's #5, so # is at the start and cuts from the start." But sometimes I even mix up this one…
    – glglgl
    Aug 31, 2016 at 14:01
  • 2
    @glglgl: On a typical USA keyboard, # is immediately to the left of $, and % is immediately to the right. No mnemonic needed --- just look down. (That's probably at least part of why those symbols were chosen.) Aug 31, 2016 at 19:17
  • @KevinJ.Chase Not everyone worldwide has a typical USA keyboard. But indeed, on a German keyboard, % is right of $ as well, that could be a start for memoizing.
    – glglgl
    Sep 1, 2016 at 7:09

4 Answers 4

32

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.

5
  • 2
    The operator supports wildcard patterns, not regular expressions.
    – gardenhead
    Aug 31, 2016 at 3:20
  • Like gardenhead mentioned - it supports glob patterns, not regular expressions. In regular expression a star means zero or more of the previous character.
    – slebetman
    Aug 31, 2016 at 5:54
  • 5
    The guide you've linked to is very much not recommended by most Unix & Linux stack exchange users. I recommend the Wooledge Bash Guide instead.
    – Wildcard
    Aug 31, 2016 at 7:13
  • 3
    The prefix and suffix removing operators are standard, so they should be usable in any sh-compatible shell, not only Bash. (though perhaps not in csh and the like).
    – ilkkachu
    Aug 31, 2016 at 8:58
  • 1
    Another context in which % is used is in format specifiers for printf. Aug 31, 2016 at 21:36
9

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.

1
  • 2
    N.B. some parameter expansions including % are posix features that many shells support.
    – kojiro
    Aug 30, 2016 at 22:34
7

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*
7

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

4
  • Maybe I got it: this is for things like .* and % defines it to be non-greedy whereas %% makes it greedy? So actually in the rename example it doesn't matter whether to use % or %% but if it were mv "$file" "${file%.*png.jpg}.jpg" (note the *) using %% would rename all files to just .jpg, right? Aug 30, 2016 at 22:46
  • @ThomasWeller I think that's correct. But I'm no bash expert.
    – DavidPostill
    Aug 30, 2016 at 22:47
  • Saying "Remove ... the shortest part of $Pattern" is wrong as the variable $Pattern is not defined in the example, only the text "Pattern" is being removed from $var when doing ${var%Pattern} or ${var%%Pattern}. Maybe this is just a typo, but it's yet another example of tldp.org being wrong. BashGuide or Bash Hackers Wiki are both much better references imho.
    – John B
    Sep 1, 2016 at 0:04
  • @JohnB Thanks. Both links bookmarked. I've corrected the text in the answer.
    – DavidPostill
    Sep 1, 2016 at 8:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .