
Мой 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
.
Если у вас find
has -print0
и xargs
has -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.