O que '{} \;' significa no contexto do comando 'encontrar'?

O que '{} \;' significa no contexto do comando 'encontrar'?

Quero excluir arquivos que possuem a extensão size = 0. Então eu tentei:

find ./ -size 0 | xargs rm

Porém, há um problema com arquivos cujos nomes começam com espaço.

Pesquisando na internet encontrei isso:

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

Funciona. Porém, acho que meu jeito de usar xargsé sofisticado demais para isso.

O que {} \;significa?

Alguém poderia me explicar?

Meu inglês não é muito bom, então use uma escrita simples.

Responder1

{}não tem absolutamente nenhum significado para bash, então é passado sem modificação como um argumento para o comando executado, aqui find.

Por outro lado, ;tem um significado específico para bash. Normalmente é usado para separar comandos sequenciais quando eles estão na mesma linha de comando. Aqui, a barra invertida \;é usada precisamente para evitar que o ponto-e-vírgula seja interpretado como um separador de comando bashe, em seguida, permitir que ele seja passado como um parâmetro para o comando subjacente, find. Citar o ponto e vírgula, ou seja, ";"ou ';', poderia ter sido uma forma alternativa de mantê-lo sem processamento.

O comando:

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

significa: encontre no diretório atual (observe que é /inútil aqui, .não pode ser nada além de um diretório) qualquer coisa que tenha tamanho 0 e para cada objeto encontrado, execute o comando rm -i name, ou seja, solicite interativamente cada arquivo se você deseja remover isto. {}é substituído por cada nome de arquivo encontrado no comando executado. Um recurso interessante é que esse nome de arquivo é estritamente um único argumento, qualquer que seja o nome do arquivo (mesmo contendo espaços incorporados, tabulações, feeds de linha e quaisquer caracteres). Este não seria o caso xargs, a menos que hacks não portáteis sejam usados. A final ;existe para encerrar a -execcláusula. A razão pela qual o seu fim precisa ser delimitado é que outras findopções podem seguir-se a -execessa, embora isso raramente seja feito. por exemplo:

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

Um problema com este comando é que ele não ignora arquivos não simples, portanto, pode solicitar que o usuário exclua soquetes, dispositivos de blocos e caracteres, canais e diretórios. Sempre falhará com o último, mesmo se você responder sim.

Outra questão, embora não seja realmente crítica aqui, é que rmserá chamado para cada arquivo que tenha tamanho zero. Se você substituir a -execterminação de /;por +, findotimizará a criação do subprocesso chamando apenas rmo número mínimo possível de vezes, geralmente apenas uma vez.

Aqui está como eu modificaria este comando:

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

Responder2

Ao usar find -exec, {}é expandido para cada resultado encontrado.

Por exemplo, se você tiver um diretório examplecontendo 3 arquivos a.txt, b.txte c.txt, find example/ -exec rm -i {} \;será expandido para:

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

O \;final é simples e escapado ;para indicar o final do padrão exec. Caso contrário, seria interpretado pelo próprio shell.

Responder3

Em conjunto com a opção finddo comando , a parte é substituída pelo nome dos arquivos encontrados quando o comando é executado. O também é importante, pois é isso que define o final do comando que está sendo executadoexec{}\;

Por exemplo

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

excluiria todos os arquivos que terminassem em .bakqualquer lugar do diretório inicial do usuário ou nas pastas contidas nele. Executando rm -fem cada arquivo encontrado.

xargspega linhas de entrada padrão, geralmente de um pipe, e forma a parte final dos argumentos dele quando executa o comando que você fornece

Responder4

Esta é uma pergunta antiga, mas quero adicionar mais algumas informações:

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

No comando anterior, \;é um ponto e vírgula com escape. Isso evita que o comando seja manipulado pelo shell (ou seja, normalmente ;separaria comandos).

O -execargumento interpreta tudo como um comando até aquele ponto e vírgula escapado \;(ou seja, rm -i {}será um comando interno a ser executado por find). Dentro do comando interno {}representa a expansão do parâmetro. Em inglês, significa “insira o nome do arquivo encontrado aqui”.

Portanto, se os arquivos encontrados fossem "arquivo-a.txt" e "arquivo-b.txt", findseria executado rm -i file-a.txtentão rm -i file-b.txt.

Um problema com este comando é que ele não ignora arquivos não simples, portanto, pode solicitar que o usuário exclua soquetes, dispositivos de blocos e caracteres, canais e diretórios. Sempre falhará com o último, mesmo se você responder sim (ou seja, os diretórios precisam ser excluídos recursivamente)

Outra questão, embora não seja realmente crítica aqui, é que rmserá chamado para cada arquivo que tenha tamanho zero. Se você substituir a -execterminação de /;por +, find otimizará a criação do subprocesso chamando apenas rmo número mínimo possível de vezes, geralmente apenas uma vez.

Aqui está como eu modificaria este comando:

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

curly bracketsou braces: {}pode ser usado de maneiras diferentes

Expansão de cinta

Chaves podem ser usadas para construir sequências:

### 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}

Também podemos combinar duas ou mais sequências:

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

Adicionando prefixos e sufixos:

### 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}

Observação:

Nenhum espaço é permitido entre chaves, {...}a menos que os espaços sejamcitadoouescapou.

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

Expansão estendida do Brace.

Chaves podem ser usadas para construir matrizes. Arrays no Bash são definidos colocando os elementos entre parênteses ()e separando cada elemento usando um espaço, assim:

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

Para acessar um elemento dentro do array, você usa seu índice entre colchetes []:

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

Portanto, podemos criar um array com algo assim:

## 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 é particularmente interessante porque agora podemos usar dec2bin para construir um conversor decimal para binário de 8 bits. Digamos que você queira saber quanto é 25 em binário. Você consegue fazer isso:

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

Mas Teo, não existem maneiras melhores de converter decimal em binário?

  • Sim, existem, mas ainda é interessante, certo?

Comandos de agrupamento

{ ... }pode ser usado para colocar uma lista de comandos a serem executados no contexto atual do shell. Nãosub-shellé criado. O ponto e vírgula ;(ou nova linha) da lista a seguir éobrigatório.

Parênteses ()são usados ​​para executar comandos em umsub-shell:

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

Não podemos obter acesso ao novo valor superbarde menu_type.

No entanto, se executarmos algo assim:

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

echo $menu_type
## output: superbar

{ ... }não cria um sub-shell, por isso podemos ter acesso ao menu_typevalor.

{ ... }também são chamados de inline group, na verdade, ele cria uma função anônima (ou seja, uma função sem nome). Em inglês simples, ao contrário de uma função "padrão", as variáveis ​​dentro de a { ... }permanecem visíveis para o restante do script.

Além disso, { ... }pode ser usado para agrupar a saída de vários comandos stdoutou para receber um redirecionamento em seu arquivo stdin. Vejamos um exemplo:

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

Agora, vamos ver como proceder com um redirecionamento de I/O no 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

Outro exemplo de como salvar a saída de um grupo de comandos em um arquivo

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

Qual é a diferença, Téo?

Bem, jovem padawan. O segundo cria o arquivo png-list.txtcom todas as saídas, começando com a linha “I found all these png files:“e terminando com a lssaída do comando.

Hack de sub-shell

Bash cria um sub-shell para um { ... }comando de grupo de chavesse e apenas sefaz parte do pipeline, por exemplo:

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

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

Observação:

Há espaço entre as chaves e a lista de comandos entre elas. Isso porque {e }são palavras reservadas (ou seja, comandos embutidos no shell). Além disso, a lista de comandos deve terminar com ponto e vírgula ;ou usar novas linhas para separar comandos.

Expansão de parâmetros

Ok, voltando ao

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

Aqui, os colchetes {}não estão sendo usados ​​como parte de um construtor de sequência, mas como uma forma de gerar expansão de parâmetros. A expansão dos parâmetros envolve o que diz na caixa:

ele pega a variável ou expressão entre colchetes e a expande para tudo o que ela representa.

O que isso significa, Teo?

Bem, isso significa ${...}que o shell expande tudo o que está dentro dele. Neste caso, monthé o array que definimos anteriormente, ou seja:

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

E o item 3dentro da matriz aponta para "Apr"(ou seja, o primeiro índice em uma matriz no Bash é [0]). Isso significa que echo ${month[3]}, após a expansão, se traduz em echo "Apr".

Interpretar uma variável como seu valor é uma forma de expandi-la, mas existem mais algumas que podemos aproveitar. Podemos usar a expansão de parâmetros para manipular o que você lê da variável (ou seja, cortando um pedaço do final).

Suponha que você tenha uma variável como:

a="This sentence is too longgg"

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

Isto pode ser útil para converter arquivos de um formato para outro. Por exemplo, se tivermos um comando que pega uma imagem JPEG chamada image.jpge a converte em uma imagem PNG chamada image.png:

convert image.jpg image.png

Podemos reescrever assim:

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

Mas como isso pode ser mais útil do que apenas escrever o nome do arquivo?

Bom, quando temos um diretório contendo centenas de imagens JPEG, você precisa converter para PNG, basta executar o seguinte nele:

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

… e todas as imagens são convertidas automaticamente. Seja bem-vindo jovem padawan.

Se você precisar cortar um pedaço do início de uma variável, em vez de %, use #:

$ a="Hello World!"

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

Espaço reservado para texto

Usado depois xargs -i(ou seja, opção de substituição de strings). As {}chaves duplas são um espaço reservado para o texto de saída.

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

Nome do caminho

Um nome de caminho é um nome de arquivo que inclui o caminho completo. Como um exemplo, /home/<user>/Notes/todo.txt. Isso às vezes é chamado de caminho absoluto. Encontraremos {}principalmente em findconstruções que contêm -exec <command> \;. No entanto, este não é um shell integrado. Se <command>contém {}, find substitui o nome do caminho completo do arquivo selecionado por "{}".

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

informação relacionada