Как рекурсивно установить права доступа к каталогу с помощью find, в котором отсутствует -exec?

Как рекурсивно установить права доступа к каталогу с помощью find, в котором отсутствует -exec?

Мой Qnap NAS проклят командой find, в которой отсутствует -execпараметр, поэтому мне нужно что-то перенаправить. Оболочка: GNU bash, версия 3.2.57(2)-release-(arm-unknown-linux-gnueabihf)

Я пытаюсь установить бит setgid для всех подкаталогов (не файлов) текущего каталога.

Это не работает:

find . -type d | xargs chmod g+s $1

Использование "$1", "$(1)"и $("1")т. д. также не будет работать. Все они указывают на то, что chmodпередается имя каталога, содержащее пробелы, в качестве двух или более параметров (он выдает стандартное сообщение справки о том, какие параметры поддерживаются).

Мне не хочется им пользоваться, xargsесли в этом нет необходимости; мне кажется, он в любом случае задыхается от длинных имен, не так ли?

Эти и их варианты не работают:

find . -type d | chmod g+s

find . -type d | chmod g+s "$1"

Я думал об использовании awkили sedдля вставки кавычек, но мне кажется, что есть более простой способ сделать это. Что люди делали раньше -exec? (Печально то, что я, вероятно, знал, еще в 1995 году или около того, но давно забыл.)

PS: Различные из этих имен каталогов будут содержать символы Unicode, символ ?и т. д. Они изначально из macOS, которая довольно разрешительна. Тем не менее, мне, вероятно, следует заменить все экземпляры ?чем-то вроде символа Unicode, чтобы Windows не подавилась ими. Но это также потребует аналогичной findоперации с этой версией crippleware find.

решение1

Вывод findвыдает имена файлов, разделенные символами новой строки 1 . Это не тот формат, который xargsхочет, и findне имеет возможности создать тот формат, который xargsхочет: он анализирует свой ввод как элементы, разделенные пробелами, с \'"использованием для кавычек. Некоторые версии xargsмогут принимать ввод, разделенный символом новой строки, но если у вас findнет стандартных опций, скорее всего, у вас xargsтоже есть.

find . -type d | xargs chmod g+sработает, пока имена ваших каталогов не содержат пробелов или \'". Обратите внимание, что нет $1: , который имеет смысл для оболочки, но оболочка не участвует в разборе вывода findи передаче его в chmod, только xargs.

Если у вас findhas -print0и xargshas -0, вы можете использовать эти параметры для передачи имен файлов, разделенных нулем, что работает с произвольными именами файлов.

find . -type d -print0 | xargs -0 chmod g+s

Если вы xargsподдерживаете стандартную опцию -I, вы можете использовать ее, чтобы указать обрабатывать каждую строку как элемент, а не строки в кавычках, разделенные пробелами. Это справляется с пробелами, но не с \"'или с переводами строк.

find . -type d | xargs -I {} chmod g+s {}

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

find . -type d | while IFS= read -r line; do chmod g+s "$line"; done

Оба эти решения работают только с именами файлов, которые не содержат символы новой строки. Вывод с именами findфайлов, содержащими символы новой строки, неоднозначен, за исключением одного случая, который трудно разобрать: findне будет спонтанно выдавать несколько слешей, поэтому если вы введете //путь к каталогу для обхода, вы сможете распознать это в выводе. Вот некоторый минимально протестированный код, использующий этот факт для преобразования вывода из findво входной формат xargs.

chars=$(printf '\t "'\\\')
{ find .//. -type d; echo .// } | LC_ALL=C sed -n '
s/['"$chars"']/\\&/g
/^\.\/\// {
x
s/\n/\\&/g
p
b
}
H' | LC_ALL=C xargs chmod g+s

1 Точнее: завершается символами новой строки (после фамилии есть символ новой строки).

решение2

Вы можете сделать рекурсию самостоятельно с помощью bash. Что-то вроде:

shopt -s nullglob dotglob
recurse_chmod () (
  cd "$1"
  for d in ./*/
  do
    if [ -L "$d" ]; then continue; fi
    chmod g+s "$d"
    recurse_chmod "$d"
  done
)
recurse_chmod .

решение3

Вы можете указать xargs использовать \0 в качестве разделителя.find . -type d -print0 | xargs -0 chmod g+s

или

find . -type d | xargs -I{} -d '\n' chmod g+s "{}". В качестве разделителя будет использоваться \n.

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