Як можна вирівняти каталог у наступному форматі?
Перед: ./aaa/bbb/ccc.png
Після: ./aaa-bbb-ccc.png
./foo/bar/baz.png
і те й ./foo-bar-baz.png
інше існує. Я припускаю, що ви не хочете замінювати останню на першу?
Як можна вирівняти каталог у наступному форматі?
Перед: ./aaa/bbb/ccc.png
Після: ./aaa-bbb-ccc.png
./foo/bar/baz.png
і те й ./foo-bar-baz.png
інше існує. Я припускаю, що ви не хочете замінювати останню на першу?
Відповіді:
Попередження: Більшість цих команд я набрав безпосередньо у своєму браузері. Кавеат лектор.
З zsh та zmv :
zmv -o -i -Qn '(**/)(*)(D)' '${1//\//-}$2'
Пояснення: Шаблон **/*
відповідає всім файлам у підкаталогах поточного каталогу, рекурсивно (він не відповідає файлам у поточному каталозі, але їх не потрібно перейменовувати). Перші дві пари круглих дужок представляють собою групи , які можуть бути позначатися як $1
і $2
в тексті заміни. Кінцева пара дужок додає D
глобальний класифікатор, щоб файли крапок не були опущені. -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
прикладах пропускається перший представлений файл / реж ... Я нарешті з’ясував, чому ви (як правило) використовували _
як $0
:) ... і дякую за чудові відповіді. Я так багато вчусь у них.
Хоча тут вже є хороші відповіді, я вважаю це більш інтуїтивним у межах bash
:
find aaa -type f -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); mv "{}" "$new"' \;
Де aaa
ваш існуючий каталог. Файли, які вони містять, будуть переміщені до поточного каталогу (залишаючи лише порожні каталоги в aaa). Два дзвінки для tr
обробки як каталогів, так і пробілів (без логіки для уникнутих косої риски - вправа для читача).
Я вважаю це більш інтуїтивно зрозумілим і відносно легким для підключення. Тобто я міг би змінити find
параметри, якщо скажу, що хочу знайти лише .png файли. Я можу змінити tr
дзвінки або додати більше за потребою. І я можу додати , echo
перш ніж , mv
якщо я хочу , щоб візуально перевірити , що він буде робити те , що потрібно (повторіть без додаткових , echo
тільки якщо все виглядає правильно:
find aaa -name \*.png -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); echo mv "{}" "$new"' \;
Зауважте також, що я використовую find aaa
..., а не find .
... або find aaa/
..., оскільки останні два залишать смішні артефакти у кінцевому імені файлу.
find . -mindepth 2 -type f -name '*' |
perl -l000ne 'print $_; s/\//-/g; s/^\.-/.\// and print' |
xargs -0n2 mv
Примітка: це не буде працювати для імені файлів, які містять \n
.
Це, звичайно, лише переміщує f
файли типу ...
Єдине зіткнення імен було б із файлів, які існували в pwd
Тестовано з цим базовим підмножиною
rm -fr junk
rm -f junk*hello*
mkdir -p junk/junkier/junkiest
touch 'hello hello'
touch 'junk/hello hello'
touch 'junk/junkier/hello hello'
touch 'junk/junkier/junkiest/hello hello'
У результаті
./hello hello
./junk-hello hello
./junk-junkier-hello hello
./junk-junkier-junkiest-hello hello
Ось 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; }
Просто передайте ім'я файлу + шлях до tr
команди.tr <replace> <with>
for FILE in `find aaa -name "*.png"`
do
NEWFILE=`echo $FILE | tr '/' '-'`
cp -v $FILE $NEWFILE
done
cp
лінію на cp -v "$FILE" "$NEWFILE"
допомогу? (Крім того, ви не повинні приймати лише файли png.)
cp
рядка недостатньо. Весь підхід до розбору find
виводу з for
циклом є недосконалим. Єдині безпечні способи використання - find
це -exec
або, -print0
якщо вони доступні (менш портативні, ніж -exec
). Я б запропонував більш надійну альтернативу, але ваше питання наразі недостатньо визначено.
Безпечний для пробілів у назви файлів:
#!/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
як імені змінної у сценарії - жахлива ідея. Крім того, ваш сценарій mangles називає імена файлів з пробілами або накидами, тому що ви неправильно використовуєте read
команду (шукайте цей сайт IFS read -r
).
find is hashed (/bin/find)
, відповідне як? Різні дистрибутиви різні?
PATH
змінної середовища, що використовується кожною системою Unix. Якщо ви пишете, /bin/find
то ваш сценарій насправді порушений на кожній системі (Gilles, шахта і, ймовірно, в ОП), яка не має /bin/find
(наприклад, b / c це є /usr/bin/find
). Вся суть PATH
змінної середовища полягає в тому, щоб це не було проблемою.
$(pwd)
вже доступний в змінної: $PWD
. Але ви не повинні використовувати абсолютний шлях тут: якщо ваш скрипт викликається, скажімо,, /home/tim
він намагається перейменувати /home/tim/some/path
на /home-tim-some-path
.