Як Bash може перейменувати серію пакунків, щоб видалити їхні версії? Я бавився з обома, expr
і %%
безрезультатно.
Приклади:
Xft2-2.1.13.pkg
стає Xft2.pkg
jasper-1.900.1.pkg
стає jasper.pkg
xorg-libXrandr-1.2.3.pkg
стає xorg-libXrandr.pkg
Як Bash може перейменувати серію пакунків, щоб видалити їхні версії? Я бавився з обома, expr
і %%
безрезультатно.
Приклади:
Xft2-2.1.13.pkg
стає Xft2.pkg
jasper-1.900.1.pkg
стає jasper.pkg
xorg-libXrandr-1.2.3.pkg
стає xorg-libXrandr.pkg
Відповіді:
Ви можете використовувати функцію розширення параметрів bash
for i in ./*.pkg ; do mv "$i" "${i/-[0-9.]*.pkg/.pkg}" ; done
Цитати потрібні для імен файлів з пробілами.
rename "s/-[0-9.]*//" *.pkg
Якщо всі файли в одному каталозі, послідовність
ls |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' |
sh
зробить вашу роботу. Команда sed створить послідовність команд mv , які потім можна передати в оболонку. Найкраще спочатку запустити конвеєр без трейлінгу | sh
, щоб переконатися, що команда виконує те, що ви хочете.
Для повторної роботи через декілька каталогів використовуйте щось на кшталт
find . -type f |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' |
sh
Зверніть увагу , що в СЕД регулярний вираз групування послідовність дужки передує зворотної косої межі, \(
і \)
, замість одиночних дужок (
і )
.
ls
в сценаріях. Першого сорту можна досягти за допомогою, printf '%s\n' * | sed ...
і з певною творчістю ви, можливо, sed
теж позбудетесь . Bash printf
пропонує '%q'
код формату, який може стати в нагоді, якщо у вас є назви файлів, які містять буквальні метахарактори оболонки.
Я зроблю щось подібне:
for file in *.pkg ; do
mv $file $(echo $file | rev | cut -f2- -d- | rev).pkg
done
припускається, що весь ваш файл знаходиться в поточному каталозі. Якщо ні, спробуйте скористатися знахідкою, як порадив вище Хав'єр.
EDIT : Крім того, ця версія не використовує жодних базових функцій, як інші, що вище, що призводить до більшої портативності.
Ось POSIX майже еквівалент прийнятої відповіді . Це торгує ${variable/substring/replacement}
розширенням лише для Bash для такого, яке доступне у будь-якій оболонці Bourne.
for i in ./*.pkg; do
mv "$i" "${i%-[0-9.]*.pkg}.pkg"
done
Розширення параметра ${variable%pattern}
створює значення variable
будь-якого суфікса, який відповідає pattern
вилученому. (Існує також ${variable#pattern}
для видалення префікса.)
Я втримався -[0-9.]*
від підтвердженої відповіді, хоча це, можливо, вводить в оману. Це не регулярний вираз, а глобальний візерунок; тож це не означає "тире з наступним нулем або більше цифр або крапок". Натомість це означає "тире, за яким йде цифра чи крапка, а за ними що-небудь". "Що-небудь" буде найкоротшим можливим поєдинком, не найдовшим. (Bash пропонує ##
і %%
для обрізки найдовшого можливого префікса чи суфікса, а не найкоротшого.)
Ми можемо припустити, що sed
він доступний на будь-якому * nix, але ми не можемо бути впевнені, що він підтримуватиме sed -n
генерування команд mv. ( ПРИМІТКА. Цеsed
робить тільки GNU .)
Незважаючи на це, bash вбудовані та sed, ми можемо швидко збити функцію оболонки для цього.
sedrename() {
if [ $# -gt 1 ]; then
sed_pattern=$1
shift
for file in $(ls $@); do
mv -v "$file" "$(sed $sed_pattern <<< $file)"
done
else
echo "usage: $0 sed_pattern files..."
fi
}
sedrename 's|\(.*\)\(-[0-9.]*\.pkg\)|\1\2|' *.pkg
before:
./Xft2-2.1.13.pkg
./jasper-1.900.1.pkg
./xorg-libXrandr-1.2.3.pkg
after:
./Xft2.pkg
./jasper.pkg
./xorg-libXrandr.pkg
Оскільки mv
автоматично не створюються цільові папки, ми не можемо використовувати нашу початкову версію sedrename
.
Це досить невелика зміна, тому було б непогано включити цю функцію:
Нам знадобиться функція утиліти abspath
(або абсолютний шлях), оскільки bash не має вбудованої програми.
abspath () { case "$1" in
/*)printf "%s\n" "$1";;
*)printf "%s\n" "$PWD/$1";;
esac; }
Як тільки ми це зробимо, ми можемо генерувати цільову папку (ів) для шаблону sed / rename, який включає нову структуру папок.
Це дозволить нам знати назви наших цільових папок. Коли ми перейменовуємось, нам потрібно буде використовувати його в імені цільового файлу.
# generate the rename target
target="$(sed $sed_pattern <<< $file)"
# Use absolute path of the rename target to make target folder structure
mkdir -p "$(dirname $(abspath $target))"
# finally move the file to the target name/folders
mv -v "$file" "$target"
Ось повний сценарій відомо про папки ...
sedrename() {
if [ $# -gt 1 ]; then
sed_pattern=$1
shift
for file in $(ls $@); do
target="$(sed $sed_pattern <<< $file)"
mkdir -p "$(dirname $(abspath $target))"
mv -v "$file" "$target"
done
else
echo "usage: $0 sed_pattern files..."
fi
}
Звичайно, він все ще працює, коли у нас теж немає конкретних цільових папок.
Якщо ми хотіли скласти всі пісні в папку, ./Beethoven/
ми можемо зробити це:
sedrename 's|Beethoven - |Beethoven/|g' *.mp3
before:
./Beethoven - Fur Elise.mp3
./Beethoven - Moonlight Sonata.mp3
./Beethoven - Ode to Joy.mp3
./Beethoven - Rage Over the Lost Penny.mp3
after:
./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3
Використовуючи цей скрипт для переміщення файлів із папок у одну папку:
Припускаючи, що ми хотіли зібрати всі відповідні файли та розмістити їх у поточній папці, ми можемо це зробити:
sedrename 's|.*/||' **/*.mp3
before:
./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3
after:
./Beethoven/ # (now empty)
./Fur Elise.mp3
./Moonlight Sonata.mp3
./Ode to Joy.mp3
./Rage Over the Lost Penny.mp3
У цьому сценарії застосовуються регулярні правила шаблону sed, ці шаблони не є PCRE (сумісні з Perl регулярні вирази). Ви можете мати синтаксис регулярного виразу регулярного вираження, використовуючи sed -r
або sed -E
залежно від вашої платформи.
Дивіться сумісність POSIX, man re_format
щоб отримати повний опис базових та розширених моделей регулярних викидів.
Я вважаю, що перейменування - набагато простіший інструмент, який можна використовувати для подібних речей. Я знайшов це на Homebrew для OSX
Для вашого прикладу я би зробив:
rename 's/\d*?\.\d*?\.\d*?//' *.pkg
'S' означає замінник. Форма - s / searchPattern / заміна / files_to_apply. Для цього вам потрібно використовувати регулярний вираз, який потребує невеликого вивчення, але варто вартих зусиль.
for
і sed
т. Д. Прекрасно, але набагато простіше і швидше просто користуватися, rename
якщо ви виявляєте, що це робите часто.
rename
команду; крім того, деякі матимуть іншу rename
команду з різними функціями, синтаксисом та / або параметрами. Це схоже на Perl, rename
який досить популярний; але відповідь справді повинна це прояснити.
краще використовувати sed
для цього щось на зразок:
find . -type f -name "*.pkg" |
sed -e 's/((.*)-[0-9.]*\.pkg)/\1 \2.pkg/g' |
while read nameA nameB; do
mv $nameA $nameB;
done
з'ясування регулярного виразу залишається як вправа (як це стосується назви файлів, які містять пробіли)
Це, здається, працює при цьому
зніміть .pkg, потім зніміть -.
for x in $(ls); do echo $x $(echo $x | sed 's/\.pkg//g' | sed 's/-.*//g').pkg; done
-e
. Напр sed -e 's/\.pkg//g' -e 's/-.*//g'
.
ls
вихід і не цитуйте змінні. Дивіться також shellcheck.net для діагностування поширених проблем та антишартов у скриптах оболонки.
У мене було кілька *.txt
перейменованих файлів, як .sql
у тій же папці. нижче працював для мене:
for i in \`ls *.txt | awk -F "." '{print $1}'\` ;do mv $i.txt $i.sql; done
Дякую за відповіді. У мене також були якісь проблеми. Переміщення файлів .nzb.queued у файли .nzb. У файлах файлів було пробіли та інша суть, і це вирішило мою проблему:
find . -type f -name "*.nzb.queued" |
sed -ne "s/^\(\(.*\).nzb.queued\)$/mv -v \"\1\" \"\2.nzb\"/p" |
sh
Він заснований на відповіді Діомідіс Шпінеліс.
Регекс створює одну групу для всього імені файлу та одну групу для частини перед .nzb.queued, а потім створює команду переміщення оболонки. З цитованими рядками. Це також уникає створення циклу в сценарії оболонки, оскільки це вже зроблено sed.
-n
варіант однаково.