¿Qué hace% en las cadenas de shell de Linux?

28

En el shell de Linux, qué hace%, como en:

for file in *.png.jpg; do
  mv "$file" "${file%.png.jpg}.jpg"
done
Nissim Nanach
fuente
44
Cada vez que busco esto, generalmente encuentro la sección correcta de la página de manual de bash buscando %%o ##, ya que es memorable y lo suficientemente raro como para encontrar la sección correcta rápidamente. man bash /##
Peter Cordes el
3
@PeterCordes Siempre recuerdo esto por "Es 5%, por lo que% está al final y, por lo tanto, se corta desde el final. Y es el # 5, por lo que # está al principio y se corta desde el principio". Pero a veces incluso mezclo esto ...
glglgl
2
@glglgl: en un teclado típico de EE. UU., #está inmediatamente a la izquierda de $, y %está inmediatamente a la derecha. No se necesita mnemónica --- solo mira hacia abajo. (Esa es probablemente al menos parte de por qué se eligieron esos símbolos.)
Kevin J. Chase
@ KevinJ.Chase No todos en el mundo tienen un teclado típico de EE. UU. Pero, de hecho, en un teclado alemán,% también tiene el valor de $, eso podría ser un comienzo para memorizar.
glglgl

Respuestas:

29

Cuando %se usa en un patrón ${variable%substring}, devolverá el contenido variablecon la menor ocurrencia de substringborrado de la parte posterior de variable.

Esta función admite patrones comodín; es por eso que acepta la estrella (asterisco) como un subsistema para cero o más caracteres.

Cabe mencionar que esto es específico de Bash: otros shells de Linux no necesariamente contienen esta función.

Si desea obtener más información sobre la manipulación de cadenas en Bash, le recomiendo leer esta página. Entre otras funciones útiles, por ejemplo, explica qué hace %%:)

Editar: Olvidé mencionar que cuando se usa en patrón $((variable%number))o $((variable1%$variable2))el %personaje funcionará como operador de módulo. DavidPostill tiene enlaces de documentación más específicos en su respuesta.

Cuando %se usa en un contexto diferente, debe reconocerse solo como carácter regular.

Marek Rost
fuente
2
El operador admite patrones comodín , no expresiones regulares.
cabeza de jardín
Como mencionó gardenhead: admite patrones globales, no expresiones regulares. En expresión regular, una estrella significa cero o más del carácter anterior.
slebetman
55
La mayoría de los usuarios de intercambio de pila de Unix y Linux no recomiendan la guía a la que se ha vinculado . Recomiendo la Guía Wooledge Bash en su lugar.
Comodín el
3
Los operadores de eliminación de prefijos y sufijos son estándar , por lo que deberían utilizarse en cualquier shshell compatible, no solo Bash. (aunque tal vez no en cshy similares).
ilkkachu
1
Otro contexto en el que %se utiliza es en especificadores de formato para printf.
Pausado hasta nuevo aviso.
9

Manual de referencia de Bash: expansión de parámetros de shell

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

La palabra se expande para producir un patrón al igual que en la expansión de nombre de archivo. Si el patrón coincide con una parte posterior del valor expandido del parámetro , entonces el resultado de la expansión es el valor del parámetro con el patrón de coincidencia más corto (el ‘%’caso) o el patrón de coincidencia más largo (el ‘%%’caso) eliminado. Si el parámetro es ‘@’o ‘*’,la operación de eliminación de patrón se aplica a cada parámetro posicional a su vez, y la expansión es la lista resultante. Si el parámetro es una variable de matriz con subíndice ‘@’ o‘*’, la operación de eliminación de patrones se aplica a cada miembro de la matriz a su vez, y la expansión es la lista resultante.

Steven
fuente
2
NB: algunas expansiones de parámetros que incluyen% son características posix que admiten muchos shells.
kojiro
7

Al experimentar, encuentro que una coincidencia después de% se descarta, cuando la cadena se encierra entre llaves (llaves).

Para ilustrar:

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

Aquí está la salida:

abcd
abcd%
abcd
abcd

ab
abcd
ab
abcd
abcd%c*
Nissim Nanach
fuente
7

En Linux shell ( bash), ¿qué hace %?

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

En este caso particular, el operador %es coincidencia de patrones (tenga en cuenta que también puede ser un operador de módulo ).


Operador de coincidencia de patrones

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

${var%$Pattern}Eliminar de $varla parte más corta de $Patterneso coincide con el extremo posterior de $var.

${var%%$Pattern}Eliminar de $varla parte más larga de $Patterneso coincide con el extremo posterior de $var.

Ejemplo: coincidencia de patrones en sustitución de parámetros

#!/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

Fuente de sustitución de parámetros


Operador de módulo

%

módulo o mod (devuelve el resto de una operación de división entera)

bash$ expr 5 % 3
2

5/3 = 1, con el resto 2

Operadores de origen

DavidPostill
fuente
Tal vez lo tengo: ¿esto es para cosas como .*y lo %define como no codicioso, mientras %%que lo hace codicioso? Entonces, en realidad, en el ejemplo de cambio de nombre, no importa si se usa %o no, %%pero si fuera mv "$file" "${file%.*png.jpg}.jpg"(tenga en cuenta el *) el uso de %% cambiaría el nombre de todos los archivos a solo .jpg, ¿verdad?
Thomas Weller
@ThomasWeller Creo que es correcto. Pero no soy un experto en bash.
DavidPostill
Decir "Eliminar ... la parte más corta de $Pattern" es incorrecto ya que la variable $Patternno está definida en el ejemplo, solo se está eliminando el texto "Patrón" $varal hacer ${var%Pattern}o ${var%%Pattern}. Tal vez esto sea solo un error tipográfico, pero es otro ejemplo de que tldp.org está equivocado. BashGuide o Bash Hackers Wiki son referencias mucho mejores en mi humilde opinión.
John B
@JohnB Gracias. Ambos enlaces marcados. He corregido el texto en la respuesta.
DavidPostill