Команды Различия с помощью цитат (Find)

Очистить кеш yum, yum очистить все , а затем yum makecache , чтобы обновить его.

Если это не помогает, скачайте новый файл определения репозитория CentOS с официального сайта. Поскольку вы используете версию 6, прошло некоторое время с момента ее установки, и определения репозитория в /etc/yum.repos.d могут указывать не на то место для получения списка зеркал.

3
10.01.2019, 22:57
1 ответ

Это (как вы заметили )довольно сложно; Я попытаюсь объяснить это. Полезно подумать о последовательности синтаксического анализа/обработки, через которую проходят команды (и ), и посмотреть, что происходит на каждом этапе. В целом процесс выглядит примерно так:

  1. Оболочка анализирует командную строку, разбивая ее на токены («слова» ), заменяя ссылки на переменные и т. д. и удаляя кавычки и escape-последовательности (после их применения ). Затем он (обычно )запускает первое "слово" как имя команды ("найти" в этих случаях )и передает ему остальные слова в качестве аргументов.
  2. findищет файлы и запускает содержимое между " -execdir" и " ;" как команды. Обратите внимание, что он заменяет " {}" совпадающим именем файла, но не выполняет никакого другого синтаксического анализа --, он просто запускает первый аргумент после " -execdir" в качестве имени команды и передает ему следующие аргументы как его аргументов.
  3. В случае, если эта команда имеет значение bashи ей передается параметр -c, она анализирует аргумент сразу после -cкак командную строку (, что-то вроде миниатюрного сценария оболочки ). ], а остальные его аргументы в качестве аргументов этого мини-скрипта -.

Хорошо, еще несколько замечаний, прежде чем я углублюсь в это :Я использую BSD find, которая требует, чтобы каталог для поиска был явно указан, поэтому я буду использовать find. -execdir...вместо просто find -execdir.... Я нахожусь в каталоге, содержащем файлы "foo.txt" и "z@$%^; *;echo wheee.jpg" (, чтобы проиллюстрировать риски неправильного использования bash -c). Наконец, у меня есть короткий скрипт с именем pargsв моем каталоге двоичных файлов, который печатает свои аргументы (или жалуется, если не получил ).

Вопрос первый:

Теперь давайте попробуем две команды из вашего первого вопроса:

$ find. -execdir pargs "{}" \;
pargs got 1 argument(s): '.'
pargs got 1 argument(s): 'foo.txt'
pargs got 1 argument(s): 'z@$%^;*;echo wheee.jpg'
$ find. -execdir "pargs {}" \;
find: pargs.: No such file or directory
find: pargs foo.txt: No such file or directory
find: pargs z@$%^;*;echo wheee.jpg: No such file or directory

Это соответствует вашим ожиданиям :первое сработало (и, кстати, вы могли бы убрать двойные -кавычки вокруг {}), а второе не удалось, потому что пробел и имя файла рассматривались как часть имя команды, а не ее аргумент.

Кстати, также можно использовать -exec[dir]... +вместо -exec[dir] \;--, это говорит findзапускать команду как можно меньше раз и передавать сразу несколько имен файлов:

$ find. -execdir pargs {} +
pargs got 3 argument(s): '.' 'foo.txt' 'z@$%^;*;echo wheee.jpg'

Вопрос второй:

На этот раз я буду выбирать варианты по одному:

$ find. -execdir bash -c "pargs" "{}" \;
pargs didn't get any arguments
pargs didn't get any arguments
pargs didn't get any arguments

«А», говоришь? Здесь происходит то, что bashзапускается со списком аргументов вроде "-c", "pargs", "foo.txt". Опция -cуказывает bashзапустить свой следующий аргумент ("pargs" )как миниатюрный сценарий оболочки, что-то вроде этого:

#!/bin/bash
pargs

...и сортировка -передает этот "мини--скрипт" аргумент "foo.txt" (подробнее об этом позже ). Но этот мини-скрипт -ничего не делает со своими аргументами (s )--, он не передает их команде pargs, поэтому pargsничего не видит. (Я расскажу, как правильно это сделать, в третьем вопросе. )Теперь давайте попробуем второй вариант второго вопроса:

$ find. -execdir bash -c "pargs {}" \;
pargs got 1 argument(s): '.'
pargs got 1 argument(s): 'foo.txt'
pargs got 1 argument(s): 'z@$%^'
bash: foo.txt: command not found
wheee.jpg

Теперь что-то вроде работает, но только вроде. bashзапускается с аргументами " -c" и "pargs " + имя файла, что работает, как и ожидалось, для "." и "foo.txt", но когда вы передаете bashаргументы "-c" и "pargs z@$%^;*;echo wheee.jpg", он теперь запускает эквивалент этого в виде мини-скрипта -:

#!/bin/bash
pargs z@$%^;*;echo wheee.jpg

Таким образом, bash разделит это на три команды, разделенные точкой с запятой:

  1. " pargs z@$%^" (которые вы видите эффект)
  2. "*", который расширяется до слов "foo.txt" и "z@$%^;*;echo wheee.jpg",и, следовательно, пытается запустить foo.txtкак команду и передать ей другое имя файла в качестве аргумента. Команды с таким именем нет, поэтому выдает соответствующую ошибку.
  3. " echo echo wheee.jpg", вполне разумная команда, и, как вы видите, она выводит на терминал "wheee.jpg".

Таким образом, это работало для файла с простым именем, но когда он столкнулся с именем файла, содержащим синтаксис оболочки, он начал пытаться выполнить части имени файла. Вот почему такой способ ведения дел не считается безопасным.

Вопрос третий:

Опять же, я буду рассматривать варианты по одному:

$ find. -execdir bash -c "pargs \"$@\"" {} \;
pargs got 1 argument(s): ''
pargs got 1 argument(s): ''
pargs got 1 argument(s): ''
$ 

Опять же, я слышу, как вы говорите: «А????» Большая проблема здесь заключается в том, что $@не экранируется и не заключен в одинарные -кавычки, поэтому он расширяется текущим контекстом оболочки до того, как будет передан в find. Я буду использовать pargs, чтобы показать, что findна самом деле получает здесь в качестве аргументов:

$ pargs. -execdir bash -c "pargs \"$@\"" {} \;
pargs got 7 argument(s): '.' '-execdir' 'bash' '-c' 'pargs ""' '{}' ';'

Обратите внимание, что $@просто исчез, потому что я запускал это в интерактивной оболочке, которая не получала никаких аргументов (и не устанавливала их с помощью setкоманды ). Таким образом, мы запускаем этот мини-скрипт -:

.
#!/bin/bash
pargs ""

...что объясняет, почему pargsполучал единственный пустой аргумент.

Если бы это было в сценарии, который имел полученные аргументы, все было бы еще более запутанным. Экранирование (или одиночное -цитирование )$решает эту проблему, но все еще не совсем работает:

$ find. -execdir bash -c 'pargs "$@"' {} \;
pargs didn't get any arguments
pargs didn't get any arguments
pargs didn't get any arguments

Проблема здесь в том, что bashобрабатывает следующий аргумент после мини-скрипта -как имя мини-скрипта -(, которое доступно для мини-скрипта -как $0, но не включено в $@), не как обычный аргумент (, т.е.$1).Вот обычный скрипт для демонстрации этого:

$ cat argdemo.sh 
#!/bin/bash
echo "My name is $0; I received these arguments: $@"
$./argdemo.sh foo bar baz
My name is./argdemo.sh; I received these arguments: foo bar baz

Теперь попробуйте это с похожим bash -cмини -скриптом:

$ bash -c 'echo "My name is $0; I received these arguments: $@"' foo bar baz
My name is foo; I received these arguments: bar baz

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

$ bash -c 'echo "My name is $0; I received these arguments: $@"' mini-script foo bar baz
My name is mini-script; I received these arguments: foo bar baz

Это именно то, что делает ваш второй вариант, передавая «bash» в качестве имени скрипта и найденное имя файла как$1:

$ find. -execdir bash -c 'pargs "$@"' bash {} \;
pargs got 1 argument(s): '.'
pargs got 1 argument(s): 'foo.txt'
pargs got 1 argument(s): 'z@$%^;*;echo wheee.jpg'

Что, наконец, работает --по-настоящему, даже для файлов со странными именами. Вот почему этот (или первый вариант в вашем первом вопросе )считается хорошим способом использования find -exec[dir]. Вы также можете использовать это с методом -exec[dir]... +:

$ find. -execdir bash -c 'pargs "$@"' bash {} +
pargs got 3 argument(s): '.' 'foo.txt' 'z@$%^;*;echo wheee.jpg'
3
27.01.2020, 21:21

Теги

Похожие вопросы