Проблема с вашей первой попыткой заключается в том, что вы пытается переименовать .
сам (текущий каталог) в дополнение ко всем подкаталогам. Пропустить это.
gfind . -mindepth 1 -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
Но на самом деле не делайте этого. никогда не вставляйте вещи в sh
. Это не работает , если имена ваших файлов не содержат специальных символов оболочки. Если у вас есть файл с именем `rm -r ~`
(вполне допустимое, хотя и необычное имя файла), то pop переходит в ваш домашний каталог. Вызов оболочки с помощью -exec
, как показано ниже.
Вторая попытка не сработает, потому что вы вставляете _
в начало полного пути, а не добавляете его в начале базового имени, то есть после последнего /
.
find . -mindepth 1 -type d -exec rename 's![^/]+$!_$&!' {} \;
Ни один из них не работает, если у вас есть вложенные каталоги, потому что они переименовывают каталоги, пока find
просматривает их. Когда вы это делаете, вам нужно воздействовать на содержимое каталога, прежде чем действовать в самом каталоге, с параметром -depth
.
find . -mindepth 1 -depth -type d -exec rename 's![^/]+$!_$&!' {} \;
Для большей переносимости вызовите оболочку и запустите mv
. Это работает в любой системе POSIX.
find . -depth -type d -name . -o -exec sh -c '
for d do mv "$d" "${d%/*}/_${d##*/}"; done
' sh {} +
Если у вас нет вложенных подкаталогов, вы можете использовать простой цикл оболочки. Если вы не хотите переименовывать каталоги, имена которых начинаются с точки, просто используйте подстановочный знак * /
, который соответствует только каталогам и символическим ссылкам на каталоги.
for d in */; do
if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi # robustness in case there are no subdirectories
if [ -L "$d" ]; then continue; fi # optional: skip symbolic links to directories
mv -- "$d" "_${d%/}"
done
Если вы хотите переименовать все каталоги, включая те, чьи имена начинаются с точки, добавьте также подстановочный знак . *
.
for d in .*/ */; do
if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi
if [ -L "$d" ]; then continue; fi
mv -- "$d" "_${d%/}"
done
Обратите внимание, что когда вы перемещаете foo
в _foo
, если уже существует каталог с именем _foo
, это перемещает старый foo
в _foo / foo
. Чтобы защититься от этого, добавьте явный тест.
Или используйте zsh. Он предустановлен на macOS. Сначала запустите autoload -U zmv
(поместите это в свой .zshrc
или в вашем скрипте), чтобы загрузить функцию zmv
, затем нерекурсивную версию (включая точечные файлы, исключая символьные ссылки - (/ D)
- это набор of квалификаторов glob ):
zmv -Q '*(/D)' '_$f'
или рекурсивная версия (с использованием модификаторов для извлечения имени каталога и базового имени):
zmv -Q '**/*(/D)' '$f:h/_$f:t'
zmv
откажется выполнять перемещение, если цель уже существует.
У вас уже есть ответ, в котором говорится, как это сделать полностью в bash с помощью цикла while ... read ...
... Я бы хотел предложить, чтобы сделать это только в bash - одно из худших способов сделать это.
Кроме того, чтение / etc / group
само по себе не является хорошей идеей, потому что этот файл является лишь одним из возможных источников данных группы (другие источники могут включать LDAP, Active Directory, winbind, NIS, mysql , pgsql, файлы базы данных Berkeley, файлы дополнительных групп из libnss-extrausers
и многие другие).
Вместо этого используйте вывод getent group
.
Попробуйте следующее:
g=( $(getent group | awk -F: '{print $3}') )
или
g=( $(getent group | cut -d: -f3) )
Метод awk
особенно полезен, если вы хотите объединить его с регулярным выражением или поиском с фиксированным текстом и / или другими критериями выбора (например, $ 1 == groupname
или $ 3> = 500 && $ 3 <= 1000
)
Кстати, например, для отладки вы можете распечатать группы в массиве g
, например:
# all on one line
echo "${g[@]}"
# one element per line
printf "%s\n" "${g[@]}"
# in a format that can be re-used to define the variable in another
# bash shell or script.
declare -p g
Мне особенно нравится declare -p
для отладки - он не только показывает имя переменной (например, похоже на echo "foo = '$ foo'"
), но также показывает индексы массива для переменных массива, и все правильно заключено в кавычки и экранировано обратной косой чертой:
$ g=( $(getent group | awk -F: '$1 ~ /my/ {print $1}') )
$ declare -p g
declare -a g='([0]="mysql" [1]="mythtv")'
BTW typeset
является синонимом declare
, который работает в bash, а также в некоторых других оболочках, таких как ksh .
Вы неудачно выбрали имя массива, которое уже зарезервировано самим bash
и является только для чтения, поэтому вы не можете его изменить.
GROUPS
Переменная массива, содержащая список групп, членом которых является текущий пользователь. является текущий пользователь. Присвоение переменной GROUPS не имеет никакого эффекта и возвращают статус ошибки. Если переменная GROUPS не установлена, она теряет свои особые свойства, даже если впоследствии он будет сброшен.
Просто используйте другое имя, и код должен работать.