PS: Пожалуйста, учтите также имена файлов с нечетными символами (например, пробелы)
Натан Дж. Б.
3
Вам также нужно будет указать, как вы хотите обработать возможный случай, когда ./foo/bar/baz.pngи то и ./foo-bar-baz.pngдругое существует. Я полагаю, вы не хотите заменить последнее на первое?
jw013
В моем случае это не имеет значения, но ответ должен действительно учитывать это, чтобы другие могли на него положиться! Спасибо!
Натан Дж. Б.
Ответы:
7
Предупреждение: большинство этих команд я набрал прямо в браузере. Будьте лектором.
Объяснение: Шаблон **/*сопоставляет все файлы в подкаталогах текущего каталога, рекурсивно (он не соответствует файлам в текущем каталоге, но их не нужно переименовывать). Первые две пары скобок - это группы, на которые можно ссылаться как $1и $2в тексте замены. В последней паре скобок добавляется Dквалификатор glob, чтобы точечные файлы не пропускались. -o -iозначает передать эту -iопцию mvтак, что вам будет предложено перезаписать существующий файл.
Только с инструментами POSIX:
find . -depth -exec sh -c '
for source; do
case $source in ./*/*)
target="$(printf %sz "${source#./}" | tr / -)";
mv -i -- "$source" "${target%z}";;
esac
done
' _ {} +
Объяснение: caseинструкция пропускает текущий каталог и подкаталоги верхнего уровня текущего каталога. targetсодержит имя исходного файла ( $0) с ./разделительной линией и заменой всех слешей на тире, а также финал z. Финал zесть в случае, если имя файла заканчивается новой строкой: в противном случае подстановка команды удалит его.
Если ваш findне поддерживает -exec … +(OpenBSD, я смотрю на вас):
find . -depth -exec sh -c '
case $0 in ./*/*)
target="$(printf %sz "${0#./}" | tr / -)";
mv -i -- "$0" "${target%z}";;
esac
' {} \;
С bash (или ksh93) вам не нужно вызывать внешнюю команду для замены слеша на тире, вы можете использовать расширение параметра ksh93 с конструкцией замены строки ${VAR//STRING/REPLACEMENT}:
find . -depth -exec bash -c '
for source; do
case $source in ./*/*)
source=${source#./}
target="${source//\//-}";
mv -i -- "$source" "$target";;
esac
done
' _ {} +
В for sourceпримерах пропущен первый представленный файл / dir ... Я наконец-то выяснил, почему вы (обычно) использовали _как $0:) ... и спасибо за отличные ответы. Я многому у них учусь.
Peter.O
Обратите внимание, что пример zmv просто выполняет пробный запуск: он выводит команды перемещения, но не выполняет их. Чтобы фактически выполнить эти команды, удалите n в -Qn.
Питер
5
Хотя здесь уже есть хорошие ответы, я нахожу это более интуитивным bash:
Где aaaнаходится ваш существующий каталог. Содержащиеся в нем файлы будут перемещены в текущий каталог (в aaa останутся только пустые каталоги). Два вызова для trобработки каталогов и пробелов (без логики для экранированных слешей - упражнение для читателя).
Я считаю это более интуитивно понятным и относительно простым в настройке. Т.е. я могу изменить findпараметры, если скажу, что хочу найти только .png файлы. Я могу изменить trзвонки или добавить больше по мере необходимости. И я могу добавить echoраньше, mvесли я хочу визуально проверить, что он будет делать правильно (затем повторять без лишнего, echoтолько если все выглядит правильно:
Заметьте также, что я использую find aaa... а не find .... или find aaa/... так как последние два оставят забавные артефакты в окончательном имени файла.
Спасибо за этот вкладыш - это хорошо сработало для меня, когда я сделал это за пределами каталога (именно это вы и сказали). Когда я попытался сделать это изнутри каталога (надеясь избежать добавления имени корневого каталога), все файлы «исчезли». Я превратил их в скрытые файлы в том же каталоге.
Примечание: это не будет работать для имени файла, которое содержит \n.
Это, конечно, только перемещает fфайлы типа ...
Единственное столкновение имен будет из файлов, ранее существовавших в pwd
Вот lsверсия .. комментарии приветствуются. Это работает для таких дурацких имен файлов
"j1ळ\n\001\n/j2/j3/j4/\nh h\n", но мне действительно интересно знать о любых подводных камнях. Это скорее упражнение в том, «может ли злобный человек lsсделать это (надежно)?»
ls -AbQR1p * | # paths in "quoted" \escaped format
sed -n '/":$/,${/.[^/]$/p}' | # skip files in pwd, blank and dir/ lines
{ bwd="$PWD"; while read -n1; # save base dir strip leading "quote
read -r p; do # read path
printf -vs "${p%\"*}" # strip trailing quote"(:)
[[ $p == *: ]] && { # this is a directory
cd "$bwd/$s" # change into new directory
rnp="${s//\//-}"- # relative name prefix
} || mv "$s" "$bwd/$rnp$s"
done; }
Это не обрабатывает нечетные имена файлов безопасно. Пробелы сломают это (среди прочего).
jw013
На первый взгляд, это чистое, удобочитаемое решение, но @ jw013 прав, оно не будет хорошо обрабатывать пробелы. Будет ли изменение cpлинии, чтобы cp -v "$FILE" "$NEWFILE"помочь? (Кроме того, вы не должны принимать только файлы png.)
Натан Дж. Б.
2
@ NathanJ.Brauer Добавление кавычек в cpстроку недостаточно. Весь подход синтаксического анализа findвывода с помощью forцикла является ошибочным. Единственными безопасными способами использования findявляются -execили, -print0если они доступны (менее переносимы, чем -exec). Я бы предложил более надежную альтернативу, но на данный момент ваш вопрос недостаточно конкретизирован.
jw013
-2
Безопасен для пробелов в именах файлов:
#!/bin/bash
/bin/find $PWD -type f | while read FILE
do
_new="${FILE//\//-}"
_new="${_new:1}"
#echo $FILE
#echo $_new
/bin/mv "$FILE" "$_new"
done
Запустил мой cpвместо mvпо понятным причинам, но должен произвести то же самое.
type find-> find is /usr/bin/findна моей машине. Использование PATHв качестве имени переменной в скрипте - ужасная идея. Кроме того, ваш скрипт искажает имена файлов пробелами или обратными слешами, потому что вы неправильно используете readкоманду (ищите этот сайт IFS read -r).
Жиль "ТАК ... перестать быть злым"
find is hashed (/bin/find)уместно как? Разные дистрибутивы разные?
Тим
Я знал, что должен был держаться подальше от этой приманки-стервятника.
Тим
@Tim Вы всегда можете удалить ответ, чтобы получить своего представителя (и мой тоже, потому что это стоит повторения, чтобы понизить). Жесткое кодирование путей в сценарии, по-видимому, указывает на то, что вам не хватает точки PATHпеременной среды, которая используется в любой системе Unix. Если вы пишете, /bin/findто ваш скрипт на самом деле не работает в каждой системе (Жиль, моя и, возможно, операционная система), которой нет /bin/find(например, в б / к он находится /usr/bin/find). Весь смысл PATHпеременной среды состоит в том, чтобы сделать это не проблемой.
jw013
Кстати, $(pwd)уже доступен в переменной: $PWD. Но вы не должны использовать абсолютный путь здесь: если ваш скрипт вызывается, скажем, из /home/tim, он пытается переименовать /home/tim/some/pathв /home-tim-some-path.
./foo/bar/baz.png
и то и./foo-bar-baz.png
другое существует. Я полагаю, вы не хотите заменить последнее на первое?Ответы:
Предупреждение: большинство этих команд я набрал прямо в браузере. Будьте лектором.
С зш и змв :
Объяснение: Шаблон
**/*
сопоставляет все файлы в подкаталогах текущего каталога, рекурсивно (он не соответствует файлам в текущем каталоге, но их не нужно переименовывать). Первые две пары скобок - это группы, на которые можно ссылаться как$1
и$2
в тексте замены. В последней паре скобок добавляетсяD
квалификатор glob, чтобы точечные файлы не пропускались.-o -i
означает передать эту-i
опциюmv
так, что вам будет предложено перезаписать существующий файл.Только с инструментами POSIX:
Объяснение:
case
инструкция пропускает текущий каталог и подкаталоги верхнего уровня текущего каталога.target
содержит имя исходного файла ($0
) с./
разделительной линией и заменой всех слешей на тире, а также финалz
. Финалz
есть в случае, если имя файла заканчивается новой строкой: в противном случае подстановка команды удалит его.Если ваш
find
не поддерживает-exec … +
(OpenBSD, я смотрю на вас):С bash (или ksh93) вам не нужно вызывать внешнюю команду для замены слеша на тире, вы можете использовать расширение параметра ksh93 с конструкцией замены строки
${VAR//STRING/REPLACEMENT}
:источник
for source
примерах пропущен первый представленный файл / dir ... Я наконец-то выяснил, почему вы (обычно) использовали_
как$0
:) ... и спасибо за отличные ответы. Я многому у них учусь.Хотя здесь уже есть хорошие ответы, я нахожу это более интуитивным
bash
:Где
aaa
находится ваш существующий каталог. Содержащиеся в нем файлы будут перемещены в текущий каталог (в aaa останутся только пустые каталоги). Два вызова дляtr
обработки каталогов и пробелов (без логики для экранированных слешей - упражнение для читателя).Я считаю это более интуитивно понятным и относительно простым в настройке. Т.е. я могу изменить
find
параметры, если скажу, что хочу найти только .png файлы. Я могу изменитьtr
звонки или добавить больше по мере необходимости. И я могу добавитьecho
раньше,mv
если я хочу визуально проверить, что он будет делать правильно (затем повторять без лишнего,echo
только если все выглядит правильно:Заметьте также, что я использую
find aaa
... а неfind .
... илиfind aaa/
... так как последние два оставят забавные артефакты в окончательном имени файла.источник
Примечание: это не будет работать для имени файла, которое содержит
\n
.Это, конечно, только перемещает
f
файлы типа ...Единственное столкновение имен будет из файлов, ранее существовавших в pwd
Протестировано с этим основным подмножеством
В результате чего
источник
Вот
ls
версия .. комментарии приветствуются. Это работает для таких дурацких имен файлов"j1ळ\n\001\n/j2/j3/j4/\nh h\n"
, но мне действительно интересно знать о любых подводных камнях. Это скорее упражнение в том, «может ли злобный человекls
сделать это (надежно)?»источник
Просто передайте имя файла + путь к
tr
команде.tr <replace> <with>
источник
cp
линии, чтобыcp -v "$FILE" "$NEWFILE"
помочь? (Кроме того, вы не должны принимать только файлы png.)cp
строку недостаточно. Весь подход синтаксического анализаfind
вывода с помощьюfor
цикла является ошибочным. Единственными безопасными способами использованияfind
являются-exec
или,-print0
если они доступны (менее переносимы, чем-exec
). Я бы предложил более надежную альтернативу, но на данный момент ваш вопрос недостаточно конкретизирован.Безопасен для пробелов в именах файлов:
Запустил мой
cp
вместоmv
по понятным причинам, но должен произвести то же самое.источник
type find
->find is /usr/bin/find
на моей машине. ИспользованиеPATH
в качестве имени переменной в скрипте - ужасная идея. Кроме того, ваш скрипт искажает имена файлов пробелами или обратными слешами, потому что вы неправильно используетеread
команду (ищите этот сайтIFS read -r
).find is hashed (/bin/find)
уместно как? Разные дистрибутивы разные?PATH
переменной среды, которая используется в любой системе Unix. Если вы пишете,/bin/find
то ваш скрипт на самом деле не работает в каждой системе (Жиль, моя и, возможно, операционная система), которой нет/bin/find
(например, в б / к он находится/usr/bin/find
). Весь смыслPATH
переменной среды состоит в том, чтобы сделать это не проблемой.$(pwd)
уже доступен в переменной:$PWD
. Но вы не должны использовать абсолютный путь здесь: если ваш скрипт вызывается, скажем, из/home/tim
, он пытается переименовать/home/tim/some/path
в/home-tim-some-path
.