Что означает '{} \;' в контексте команды 'find'?

Что означает '{} \;' в контексте команды 'find'?

Я хочу удалить файлы, которые имеют size = 0. Поэтому я попробовал:

find ./ -size 0 | xargs rm

Однако возникают проблемы с файлами, имена которых начинаются с пробела.

Поискав в интернете, я нашел это:

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

Это работает. Однако, я думаю, что мой способ использования xargsслишком сложен для этого.

Что {} \;значит?

Может ли кто-нибудь мне объяснить?

Мой английский не очень хорош, поэтому, пожалуйста, пишите простыми словами.

решение1

{}не имеет абсолютно никакого значения bash, поэтому передается без изменений как аргумент выполняемой команды, здесь find.

С другой стороны, ;имеет определенное значение для bash. Обычно он используется для разделения последовательных команд, когда они находятся в одной командной строке. Здесь обратная косая черта в \;используется именно для того, чтобы предотвратить интерпретацию точки с запятой как разделителя команд, bashа затем разрешить ее передачу в качестве параметра базовой команде, find. Заключение точки с запятой в кавычки, то есть ";"или ';', могло бы быть альтернативным способом оставить ее необработанной.

Команда:

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

означает: найти в текущем каталоге (обратите внимание, что /здесь бесполезен, .в любом случае не может быть ничем иным, как каталогом) все, что имеет размер 0, и для каждого найденного объекта выполнить команду , rm -i nameт. е. интерактивно запросить для каждого файла, если вы хотите его удалить. {}заменяется каждым именем файла, найденным в выполненной команде. Одной из приятных особенностей является то, что это имя файла является строго одним аргументом, каким бы ни было имя файла (даже содержащим пробелы, табуляции, переводы строк и любые другие символы). Это не относится к xargs, если только не используются непереносимые хаки. final ;находится там, чтобы закончить -execпредложение. Причина, по которой его конец должен быть разделен, заключается в том, что за ним findмогут следовать другие параметры -exec, хотя это делается редко. например:

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

Одна из проблем с этой командой заключается в том, что она не игнорирует не простые файлы, поэтому может предложить пользователю удалить сокеты, блочные и символьные устройства, каналы и каталоги. С последним всегда будет ошибка, даже если вы ответите «да».

Другая проблема, хотя и не очень критичная здесь, заключается в том, что rmбудет вызываться для каждого файла, имеющего нулевой размер. Если вы замените -execокончание с /;на +, findоптимизирует создание подпроцесса, вызывая только rmминимально возможное количество раз, часто всего один раз.

Вот как я бы изменил эту команду:

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

решение2

При использовании find -execрасширяется {}до каждого найденного результата.

Например, если у вас есть каталог, exampleсодержащий 3 файла a.txt, b.txtи c.txt, find 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 \;

В \;конце просто экранируется, ;чтобы обозначить конец шаблона exec. В противном случае он будет интерпретирован самой оболочкой.

решение3

В сочетании с опцией findкоманды часть заменяется на имя файлов, найденных при выполнении команды. Это также важно, поскольку это то, что определяет конец выполняемой командыexec{}\;

Например

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

удалит все файлы, заканчивающиеся на .bakв любом месте домашнего каталога пользователя или в папках, содержащихся внутри него. Выполняя rm -fдля каждого найденного файла.

xargsпринимает стандартные входные строки, обычно из конвейера, и формирует из них хвостовую часть аргументов, когда он выполняет команду, которую вы ему даете

решение4

Это старый вопрос, но я хочу добавить немного информации:

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

В предыдущей команде \;это экранированная точка с запятой. Это предотвращает обработку команды оболочкой (т.е. обычно ;разделяет команды).

Аргумент -execинтерпретирует все как команду до этой экранированной точки с запятой \;(т.е. rm -i {}это будет внутренняя команда, которая будет выполнена find). Внутри внутренней команды {}представляют собой расширение параметра. На языке pain это означает «вставьте имя файла, найденное здесь».

Таким образом, если найденные файлы были «file-a.txt» и «file-b.txt», то findбудет выполнен .rm -i file-a.txtrm -i file-b.txt

Одна из проблем этой команды заключается в том, что она не игнорирует не простые файлы, поэтому может предложить пользователю удалить сокеты, блочные и символьные устройства, каналы и каталоги. С последним всегда будет ошибка, даже если вы ответите «да» (т. е. каталоги нужно удалять рекурсивно)

Другая проблема, хотя и не очень критичная здесь, заключается в том, что rmбудет вызываться для каждого файла, имеющего нулевой размер. Если вы замените -execокончание с /;на +, find оптимизирует создание подпроцесса, вызывая только rmминимально возможное количество раз, часто всего один раз.

Вот как я бы изменил эту команду:

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

curly bracketsили braces: {}может использоваться по-разному

Расширение скобок

Скобки можно использовать для построения последовательностей:

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

Также мы можем объединить две или более последовательностей:

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

Добавление префиксов и суффиксов:

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

Примечание:

Внутри скобок не допускается использование пробелов, {...}если только они нецитируетсяилисбежал.

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

Расширение расширенной скобы.

Скобки можно использовать для построения массивов. Массивы в Bash определяются путем помещения элементов в скобки ()и разделения каждого элемента пробелом, например:

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

Чтобы получить доступ к элементу массива, используйте его индекс в квадратных скобках []:

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

Поэтому мы можем создать массив примерно следующим образом:

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

Последнее особенно интересно, потому что теперь мы можем использовать dec2bin для построения 8-битного десятично-двоичного преобразователя. Допустим, вы хотите узнать, чему равно 25 в двоичном виде. Вы можете сделать это:

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

Но разве нет лучших способов преобразования десятичных чисел в двоичные?

  • Да, есть, но все равно интересно, не правда ли?

Группировка команд

{ ... }может использоваться для размещения списка команд, которые будут выполнены в текущем контексте оболочки. Нетсуб-оболочкасоздается. Точка с запятой ;(или новая строка) после списканеобходимый.

Скобки ()используются для запуска команд всуб-оболочка:

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

Мы не можем получить доступ к новому superbarзначению menu_type.

Однако, если мы запустим что-то вроде этого:

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

echo $menu_type
## output: superbar

{ ... }не создает дочернюю оболочку, поэтому мы можем получить доступ к значению menu_type.

{ ... }также упоминаются как inline group, по сути, он создает анонимную функцию (т. е. функцию без имени). Проще говоря, в отличие от «стандартной» функции, переменные внутри остаются { ... }видимыми для остальной части скрипта.

Также { ... }может использоваться для группировки вывода из нескольких команд в stdoutили для получения перенаправления в его stdin. Давайте рассмотрим пример:

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

Теперь давайте посмотрим, как выполнить перенаправление ввода-вывода в группе 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

Еще один пример сохранения вывода группы команд в файл

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

В чем разница, Тео?

Ну что ж, юный падаван. Второй создает файл png-list.txtсо всеми выходами, начиная со строки “I found all these png files:“, и заканчивая lsвыводом команды.

Взлом подоболочки

Bash создает подоболочку для { ... }команды группы фигурных скобокесли и только еслиэто часть конвейера, например:

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

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

Примечание:

Между скобками и списком команд, заключенным в них, есть пробел. Это потому, что {и }являются зарезервированными словами (т. е. командами, встроенными в оболочку). Кроме того, список команд должен заканчиваться точкой с запятой ;или использовать новые строки для разделения команд.

Расширение параметров

Ладно, вернемся к

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

Здесь скобки {}не используются как часть конструктора последовательности, а как способ генерации расширения параметров. Расширение параметров подразумевает то, что написано на коробке:

он берет переменную или выражение в фигурных скобках и расширяет ее до того значения, которое она представляет.

Что это значит, Тео?

Ну, это значит, что ${...}он говорит оболочке развернуть то, что внутри нее. В этом случае, monthэто массив, который мы определили ранее, то есть:

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

И элемент 3в массиве указывает на "Apr"(т.е. первый индекс в массиве в Bash — это [0]). Это означает echo ${month[3]}, что после расширения преобразуется в echo "Apr".

Интерпретация переменной как ее значения — один из способов ее расширения, но есть еще несколько, которые мы можем использовать. Мы можем использовать расширение параметра, чтобы манипулировать тем, что вы считываете из переменной (например, отрезая кусок от конца).

Предположим, у вас есть такая переменная:

a="This sentence is too longgg"

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

Это может быть полезно для преобразования файлов из одного формата в другой. Например, если у нас есть команда, которая берет изображение JPEG image.jpgи преобразует его в изображение PNG с именем image.png:

convert image.jpg image.png

Мы можем переписать это так:

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

Но чем это может быть полезнее, чем просто указание имени файла?

Ну, когда у нас есть каталог, содержащий сотни изображений JPEG, которые нужно преобразовать в PNG, просто выполните в нем следующее:

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

… и все картинки автоматически конвертируются. Добро пожаловать, юный падаван.

Если вам нужно отрезать часть от начала переменной, вместо %, используйте #:

$ a="Hello World!"

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

Заполнитель для текста

Используется после xargs -i(т.е., опция замены строк). {}Двойные фигурные скобки являются заполнителем для выходного текста.

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

Имя пути

Имя пути — это имя файла, включающее полный путь. Например, /home/<user>/Notes/todo.txt. Иногда его называют абсолютным путем. Мы будем встречать его {}в основном в findконструкциях, содержащих -exec <command> \;. Однако это не встроенная команда оболочки. Если <command>содержит {}, то find заменяет полное имя пути выбранного файла на "{}".

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

Связанный контент