Que hace '{} \;' ¿Qué significa en el contexto del comando 'buscar'?

Que hace '{} \;' ¿Qué significa en el contexto del comando 'buscar'?

Quiero eliminar archivos que tienen size = 0. Entonces lo intenté:

find ./ -size 0 | xargs rm

Pero tiene un problema con los archivos en los que los nombres comienzan con un espacio.

Buscando en internet encontré esto:

find ./ -size 0 -exec rm -i {} \;

Funciona. Sin embargo, creo que mi forma de usarlo xargses demasiado sofisticada para esto.

¿Que {} \;significa?

¿Alguien podría explicarme?

Mi inglés no es muy bueno, así que utilice una escritura sencilla.

Respuesta1

{}no tiene absolutamente ningún significado bash, por lo que se pasa sin modificar como argumento del comando ejecutado, aquí find.

Por otro lado, ;tiene un significado específico bash. Normalmente se utiliza para separar comandos secuenciales cuando están en la misma línea de comando. Aquí, la barra invertida \;se usa precisamente para evitar que el punto y coma se interprete como un separador de comando bashy luego permitir que se pase como parámetro al comando subyacente find. Citar el punto y coma, es decir, ";"o ';', podría haber sido una forma alternativa de no procesarlo.

El comando:

find ./ -size 0 -exec rm -i {} \;

significa: busque en el directorio actual (tenga en cuenta que es /inútil aquí, .no puede ser más que un directorio de todos modos) cualquier cosa que tenga un tamaño de 0 y para cada objeto encontrado, ejecute el comando rm -i name, es decir, solicite interactivamente cada archivo si desea eliminar él. {}se reemplaza por cada nombre de archivo que se encuentra en el comando ejecutado. Una característica interesante es que este nombre de archivo es estrictamente un argumento único, cualquiera que sea el nombre del archivo (incluso si contiene espacios incrustados, tabulaciones, avances de línea y cualquier carácter). Este no sería el caso con xargs, a menos que se utilicen hacks no portátiles. La final ;está ahí para poner fin a la -execcláusula. La razón por la que es necesario delimitar su fin es que otras findopciones podrían seguir a -execuna, aunque rara vez se hace. p.ej:

find . -name "*.js" -exec ls -l {} \; -name "special*" -exec wc -l {} \;

Un problema con este comando es que no ignorará los archivos que no sean simples, por lo que podría solicitar al usuario que elimine sockets, dispositivos de bloques y caracteres, tuberías y directorios. Siempre fallará con este último incluso si respondes que sí.

Otro problema, aunque no realmente crítico aquí, es que rmse llamará para cada archivo que tenga un tamaño cero. Si sustituye la -execterminación de /;a +, findoptimizará la creación del subproceso llamando solo rmel mínimo número posible de veces, a menudo solo una vez.

Así es como modificaría este comando:

find . -type f -size 0 -exec rm -i {} +

Respuesta2

Al utilizar find -exec, {}se expande a cada resultado encontrado.

Por ejemplo, si tiene un directorio que examplecontiene 3 archivos a.txty b.txt, se expandirá a:c.txtfind example/ -exec rm -i {} \;

find example/ -exec rm -i example/a.txt \;
find example/ -exec rm -i example/b.txt \;
find example/ -exec rm -i example/c.txt \;

El \;final es simple y tiene un escape ;para indicar el final del patrón ejecutivo. De lo contrario, sería interpretado por el propio shell.

Respuesta3

Junto con la opción finddel comando exec, la {}parte se reemplaza por el nombre de los archivos encontrados cuando se ejecuta el comando. El \;también es importante, porque es lo que define el final del comando que se ejecuta.

Por ejemplo

find ~ -name \*.bak -exec -rm -f {} \;

eliminaría todos los archivos que terminen en .bakcualquier lugar del directorio de inicio del usuario o en las carpetas contenidas en él. Ejecutando rm -fsobre cada archivo encontrado.

xargstoma líneas de entrada estándar, generalmente de una tubería, y forma la parte final de los argumentos cuando ejecuta el comando que usted le da.

Respuesta4

Esta es una vieja pregunta pero quiero agregar más información:

find ./ -size 0 -exec rm -i {} \;

En el comando anterior, \;es un punto y coma escapado. Esto evita que el comando sea manejado por el shell (es decir, normalmente ;separaría los comandos).

El -execargumento interpreta todo como un comando hasta ese punto y coma escapado \;(es decir, rm -i {}será un comando interno que será ejecutado por find). Dentro del comando interno, {}representan la expansión del parámetro. En inglés, significa "inserte el nombre del archivo que se encuentra aquí".

Entonces, si los archivos encontrados fueran "archivo-a.txt" y "archivo-b.txt", findse ejecutarían rm -i file-a.txtentonces rm -i file-b.txt.

Un problema con este comando es que no ignorará los archivos que no sean simples, por lo que podría solicitar al usuario que elimine sockets, dispositivos de bloques y caracteres, tuberías y directorios. Siempre fallará con este último incluso si responde que sí (es decir, los directorios deben eliminarse de forma recursiva)

Otro problema, aunque no realmente crítico aquí, es que rmse llamará para cada archivo que tenga un tamaño cero. Si sustituye la -execterminación de /;a +, find optimizará la creación del subproceso llamando solo rmel mínimo número posible de veces, a menudo solo una vez.

Así es como modificaría este comando:

find ./ -type f -size 0 -exec rm -i {} +

curly bracketso braces: {}se puede utilizar de diferentes maneras

Expansión de llaves

Se pueden utilizar llaves para construir secuencias:

### prints out the numbers from 0 to 10
echo {0..10}

## prints out the same numbers, but in reverse order
echo {10..0}

## prints every second number, from 10 to 0
echo {10..0..2}

## prints every second letter, from z and working its way backwards to a.
echo {z..a..2}

También podemos combinar dos o más secuencias:

## prints out a pair of letters, from aa to zz.
echo {a..z}{a..z}

Agregar prefijos y sufijos:

### adds '"' as prefix and suffix
echo \"{These,words,are,quoted}\"
# output: "These" "words" "are" "quoted"

# concatenates the files file1, file2, and file3 into combined_file.
cat {file1,file2,file3} > combined_file

# copies "file22.txt" to "file22.backup"
cp file22.{txt,backup}

Nota:

No se permiten espacios entre llaves {...}a menos que los espacios seancitadooescapado.

echo {file1,file2}\ :{\ A," B",' C'}
# output: file1 : A file1 : B file1 : C file2 : A file2 : B file2 : C

Expansión de soporte extendido.

Se pueden utilizar llaves para construir matrices. Los arreglos en Bash se definen poniendo elementos entre paréntesis ()y separando cada elemento usando un espacio, así:

month=("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")

Para acceder a un elemento dentro de la matriz, utiliza su índice entre paréntesis []:

 # Array indexes start at [0], so [3] points to the fourth item
$ echo ${month[3]}
## output: Apr

Por lo tanto, podemos crear una matriz con algo como esto:

## builds an array that contains all the 2-letter combinations of the entire alphabet.
letter_combos=({a..z}{a..z})

## contains all the binary numbers for an 8-bit register, in ascending order,
## from 00000000, 00000001, 00000010, etc., to 11111111. 
dec2bin=({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1})

Este último es particularmente interesante porque ahora podemos usarlo con dec2bin para construir un convertidor de decimal a binario de 8 bits. Digamos que quieres saber qué es 25 en binario. Puedes hacerlo:

$ echo ${dec2bin[25]}
## output: 00011001

Pero Teo, ¿no hay mejores formas de convertir decimal a binario?

  • Sí, los hay, pero no deja de ser interesante, ¿verdad?

Comandos de agrupación

{ ... }se puede utilizar para colocar una lista de comandos que se ejecutarán en el contexto actual del shell. Nosubcapaes creado. La ;siguiente lista de punto y coma (o nueva línea) esrequerido.

Los paréntesis ()se utilizan para ejecutar comandos en unsubcapa:

menu_type=bar
echo $menu_type
## output: bar

## new lets called in a sub-shell
(menu_type=superbar; echo $menu_type)
## output: superbar

## back to the context
echo $menu_type
## output: bar

No podemos acceder al nuevo valor superbarde menu_type.

Sin embargo, si ejecutamos algo como esto:

{ menu_type=superbar; echo $menu_type; }
## output: superbar

echo $menu_type
## output: superbar

{ ... }no crea un sub-shell, es por eso que podemos obtener acceso al menu_typevalor.

{ ... }también se conocen como inline group, de hecho, crea una función anónima (es decir, una función sin nombre). En términos sencillos, a diferencia de una función "estándar", las variables dentro de a { ... }permanecen visibles para el resto del script.

Además, { ... }se puede utilizar para agrupar la salida de varios comandos stdouto para recibir una redirección a su archivo stdin. Veamos un ejemplo:

#!/bin/bash
# rpm-check.sh
#  Queries an rpm file for description, listing, and whether it can be installed.
#  Saves output to a file.

SUCCESS=0
E_NOARGS=65

if [ -z "$1" ]; then
  echo "Usage: `basename $0` rpm-file"
  exit $E_NOARGS
fi  

{ # Begin command group.
  echo
  echo "Archive Description:"
  rpm -qpi $1       # Query description.
  echo
  echo "Archive Listing:"
  rpm -qpl $1       # Query listing.
  echo
  rpm -i --test $1  # Query whether rpm file can be installed.
  if [ "$?" -eq $SUCCESS ]
  then
    echo "$1 can be installed."
  else
    echo "$1 cannot be installed."
  fi  
  echo              # End command group.
} > "$1.test"       # Redirects output of everything in block to file.

echo "Results of rpm test in file $1.test"

exit 0

Ahora, veamos cómo proceder con una redirección de E/S en el grupo stdin:

#!/bin/bash
File=/etc/fstab

## reads the first two lines of the file
{
  read line1
  read line2
} < $File

echo "First line in $File is:"
echo "$line1"
echo
echo "Second line in $File is:"
echo "$line2"

exit 0

Otro ejemplo de cómo guardar la salida de un grupo de comandos en un archivo

## exec commands sequentially and redirects the output of the ls command into the png-list.txt file
echo "I found all these png files:"; find . -iname "*.png"; echo "Within this bunch of files:"; ls > png-list.txt

## exec commands sequentially and redirects the output of the group into the png-list.txt file
{ echo "I found all these png files:"; find . -iname "*.png"; echo "Within this bunch of files:"; ls; } > png-list.txt

¿Cuál es la diferencia, Teo?

Bueno, joven padawan. El segundo crea el archivo png-list.txtcon todas las salidas, comenzando con la línea “I found all these png files:“y terminando con la lssalida del comando.

Hack de subcapa

Bash crea un subshell para un { ... }comando de grupo de llavessi y solo sies parte de la tubería, por ejemplo:

$ { A=1; { A=2; sleep 2; } ; echo $A; }
## output: 2

$ { A=1; { A=2; sleep 2; } | sleep 1; echo $A; }
## output: 1

Nota:

Hay espacio entre las llaves y la lista de comandos contenida dentro de ellas. Esto se debe a que {y }son palabras reservadas (es decir, comandos integrados en el shell). Además, la lista de comandos debe terminar con un punto y coma ;o usar nuevas líneas para separar los comandos.

Ampliación de parámetros

Bien, volviendo a

month=("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")
echo ${month[3]}
## output: Apr

Aquí las llaves {}no se utilizan como parte de un generador de secuencias, sino como una forma de generar expansión de parámetros. La expansión de parámetros implica lo que dice en el cuadro:

toma la variable o expresión entre llaves y la expande a lo que representa.

¿Qué significa esto Teo?

Bueno, significa que ${...}le dice al caparazón que expanda todo lo que hay dentro de él. En este caso, monthes la matriz que definimos anteriormente, es decir:

month=("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")

Y el elemento 3dentro de la matriz apunta a "Apr"(es decir, el primer índice de una matriz en Bash es [0]). Eso significa que echo ${month[3]}, después de la expansión, se traduce en eco "Apr".

Interpretar una variable como su valor es una forma de expandirla, pero hay algunas más que podemos aprovechar. Podemos usar la expansión de parámetros para manipular lo que lee de la variable (es decir, cortando un trozo del final).

Supongamos que tiene una variable como:

a="This sentence is too longgg"

## chops off the last two gs
echo ${a%gg}
## output: This sentence is too long

Esto puede resultar útil para convertir archivos de un formato a otro. Por ejemplo, si tenemos un comando que toma una imagen JPEG llamada image.jpgy la convierte en una imagen PNG llamada image.png:

convert image.jpg image.png

Podemos reescribirlo así:

i='image.jpg'
## chops off the extension 'jpg' and adds 'png'
convert $i "${i%jpg}png"
## output: convert image.jpg image.png

Pero ¿cómo puede ser esto más útil que simplemente escribir el nombre del archivo?

Bueno, cuando tengamos un directorio que contenga cientos de imágenes JPEG, necesitemos convertirlas a PNG, simplemente ejecute lo siguiente en él:

for i in *.jpg; do convert $i ${i%jpg}png; done

… y todas las imágenes se convierten automáticamente. De nada, joven padawan.

Si necesita cortar un trozo del principio de una variable, en lugar de %, utilice #:

$ a="Hello World!"

## chops off the word 'Hello' and adds 'Goodbye'
$ echo Goodbye${a#Hello}
## output: Goodbye World!

Marcador de posición para texto

Usado después xargs -i(es decir, opción de reemplazar cadenas). Las {}llaves dobles son un marcador de posición para el texto de salida.

## Execute 'echo ./<file>' for each file in the directory
ls . | xargs -i -t echo ./{} $1
#            ^^         ^^

Nombre de ruta

Un nombre de ruta es un nombre de archivo que incluye la ruta completa. Como ejemplo, /home/<user>/Notes/todo.txt. A esto a veces se le llama camino absoluto. Nos encontraremos {}principalmente en findconstrucciones que contienen -exec <command> \;. Sin embargo, esto no es un shell incorporado. Si <command>contiene {}, buscar sustituye el nombre de ruta completo del archivo seleccionado por "{}".

# Removes all core dump files from user's home directory.
find ~/ -name 'core*' -exec rm {} \;

información relacionada