Видалити пробіли, дефіси та підкреслення у назви файлів?


10

Яка хороша команда для видалення пробілів, дефісів та підкреслення з усіх файлів у каталозі чи вибраних файлів?

Я використовую таку команду з налаштуваннями Thunar Custom Actions для прослуховування файлових файлів:

for file in %N; do mv "$file" "$(echo "$file" | tr -s ' ' | tr ' A-Z' '-a-z' | tr -s '-' | tr -c '[:alnum:][:cntrl:].' '-')"; done

Але ця команда замінює пробіли лише тире / дефісами та малими літерами.

Я використовував таку команду в терміналі, щоб видалити пробіли з тисяч імен файлів у папці, і вона працювала досить швидко:

 rename "s/ //g" *

Знову ж таки, він видаляє лише пробіли, а не дефіси / дефіси та підкреслення.

В ідеалі я не хочу пробілів, дефісів і підкреслень у своїх іменах. І було б чудово, якби команду можна було використовувати з Thunar Custom Actions на вибраних файлах.


2
Зауважу, одну проблему, яку має багато запропонованих рішень, є неправильна перевірка існування "нового" імені перед тим, як перемістити файл. Якщо це не зробити, це може стати потенційним джерелом безлічі проблем.
— mdpc

Чи можна змінити команду John1024, щоб перевірити це?
— користувач8547

@ user8547rename -i "s/[-_ ]//g" *
— Sparhawk

Дякую Спархак. Між іншим, для тих, хто зацікавлений у використанні цього в якості спеціальної дії Thunar, командою для Thunar є: для файлу у% N; зробити mv "$ файл" echo $file | sed -e 's/[ _-]//g'; зроблено
— user8547

Відповіді:


11

Версія renameцього perlпакета підтримує регулярні вирази:

rename "s/[-_ ]//g" *

Крім того,

rename -i "s/[-_ ]//g" *

-iПрапор буде зробити renameвикористовувати інтерактивний режим, спонукаючи , якщо мета вже існує, замість того , щоб мовчки перезапис.

Перейменування Perl іноді називають prename.

Перейменування Perl проти перейменування util-linux

У системах, схожих на Debian, перейменування perl видається за замовчуванням, і вищезазначені команди повинні просто працювати.

У деяких дистрибутивах renameутиліта від util-linux є типовою. Ця утиліта абсолютно не сумісна з Perl's rename.

  • Все: Спочатку перевірте, чи Perl's renameдоступний під назвою prename.

  • Debian: Перейменування Perl має бути типовим. Він також доступний як prename. Хоча renameвиконуваний файл знаходиться під контролем /etc/alternativesі, таким чином, він міг бути змінений на щось інше.

  • archlinux: Запустити pacman -S perl-renameі команда доступна як perl-rename. Для більш зручного імені створіть псевдонім. (Наконечник капелюха: ChiseledAbs)

  • Mac OSX Відповідно до цієї відповіді , renameможна встановити на OSX за допомогою домашньої мови через:

    brew install rename 
  • Пряме завантаження: rename також доступне від Perl Monks:

     wget 'http://www.perlmonks.org/?displaytype=displaycode;node_id=303814' -O rename

Я думаю, це залежить від того, про що renameти говориш. Один з util-linux -2.24.2-1.fc20.x86_64 не підтримує регулярні вирази.
— Cristian Ciupitu

1
@ CristianCiupitu Я щойно перевірив сторінку чоловіка на предмет знайденої вами версії перейменування. Виходячи з аргументів, версія, renameяку використовував ОП, виглядає як perlверсія, а не util-linuxверсія.
— John1024

Для запису це renameсторінка man для версії util-linux . У будь-якому разі, окрім цієї ноти, важливим є те, що ОП отримала свою відповідь (а ви від мене звернулися :-D).
— Cristian Ciupitu

@ CristianCiupitu Дякую за те, що знайшли це. Зверніться до вас +1.
— John1024,

1
@ John1024 archlinux, але я з'ясував, як, просто переходьте, pacman -S perl-renameя думаю, ви можете псевдонім.
— ChiseledAbs

5

Я б замінив усі ці trкоманди sedкомандою заміщення, наприклад:

for file in %N; do 
    mv "$file" "$(echo "$file" | sed 's/[ _-]//g')"
done

4

Якщо не рахувати mv, то для цього вам взагалі не потрібен зовнішній процес - ви можете просто зафіксувати їх.

ifsqz() ( LC_ALL=C sqz=$1
    isf() { [ -e "$1" ] || [ -L "$1" ] ; }  
    set -- * ; set -f
    for f do isf "$f" || break
    IFS=$sqz; set -- $f; IFS=
    isf "$*" || mv -- "$f" "$*"
    done
)

Тим не менш, це означає mvвиклик на файл, і так, мабуть rename, краще. Хоча це повинно працювати лише з урахуванням POSIX mvв $PATHі оболонки POSIX.

Отже, я придумав для цього якусь божевільну демонстрацію. Тестовий набір формується так:

tee - - - - <<CGEN |\
dd cbs=90 conv=unblock |\
sed 'G;$!N'";s/^/touch -- '/;s/$/'/" |sh
$( #BEGIN CGEN
   LC_ALL=C
   i= n='"$((i=((i=i+1)==10||i==39||i==47)>0?(i+1):i))"'
   printf '%b -_   ---___'  $(
   IFS=0; eval \
       printf '"\\\\%04o\\\\%04o "' "$(
       printf "$n"' "$i" '%s $(
       printf %.252d
#END
))"))
CGEN

В першу чергу я першим визнаю, що вищевказана команда дає результати, які можна легше отримати іншими способами. Але інші засоби, ймовірно, не продемонструють також, що можна зробити з $IFSневеликою (хворою?) Уявою.

Тож перший біт є досить прямим:

  • tee випускає 5 примірників свого вводу - називається гередокумент CGEN

  • dd блокує його введення новими рядками в 90 байт на блок і труби, що ...

  • sedприєднується до 2 цих блоків на двох \nсимволах ewline, одноцитуючи 'результати, і попереджує рядок touch --для кожного циклу рядків перед тим, як виводити на ...

  • sh який потім виконує весь вхід як команди оболонки

#CGENТрохи , хоча ... Ну, коротше ...

  • внизу printfдрукується 252 0s

  • наступний з останнього отримує 252 ''аргументи з нульовим рядком і для кожного друкує вміст, за $nяким слідує рядок" $i "

  • evalінтерпретує аргументи наступного, printfперш ніж він надрукує результати цієї інтерпретації, як вісімкові числа, попередньо висунуті двома накидами на шматок

  • остання printfдрукує значення байтів для цих восьмериків 2 одночасно з наступним рядком -_ ---___для кожної пари

  • $nініціалізується до рівняння, яке збільшуватиметься $iна одиницю для кожної оцінки, за винятком того, що воно пропускає значення 10, 39 або 47 - (які є \nпрямою лінією, одноцитатою 'та /косою рисою в ASCII десятичному відповідно)

Кінцевим результатом є каталог, що містить безліч по-справжньому потворних імен файлів, що містять кожен байт у моєму наборі від 1 до 255, за винятком одинарної цитати (лише пропущеної, щоб уникнути ще однієї sed s///заяви) та /косої риски. Ці назви файлів виглядають приблизно так:

(set -- *; printf '%s\n\n##############\n\n%s\n' "${9}" "${34}")  | cat -A

   ---___ww -_   ---___xx -_   ---___yy -_   ---___zz -_   ---___{{ -_   ---___|| -_   ---$
$
___}} -_   ---___~~ -_   ---___^?^? -_   ---___M-^@M-^@ -_   ---___M-^AM-^A -_   ---___M-^BM-^B -_   ---___M-^CM-^C$
$
##############$
$
 -_   ---___M-ZM-Z -_   ---___M-[M-[ -_   ---___M-\M-\ -_   ---___M-]M-] -_   ---___M-^M-^ -_   ---___M-_M-_ -_$
$
---___M-`M-` -_   ---___M-aM-a -_   ---___M-bM-b -_   ---___M-cM-c -_   ---___M-dM-d -_   ---___M-eM-e -_   ---___$

Тепер я отримаю деякі дані про ці файли:

chksqz() ( LC_ALL=C sqz=$1
    set -- * ; set -f ; IFS= ; tc="$*"
    printf '#%s\n' \
        "There are $# files in this test directory." \
        "All filenames combined contain a total of ${#tc} bytes."
    IFS=$sqz ; set -- $* ; IFS= ; sc="$*"  
    printf "%s '$sqz'" \
        "#Of which ${#sc} bytes are not"\
        " and $((${#tc}-${#sc})) bytes are"
    set +f ; unset IFS
    printf ".\n#%s\n#Total:\t%d\n#Other:\t%d\n#'$sqz':\t%d\n" \
        "And to confirm these figures:" \
        $(  printf %s * | wc -c 
            printf %s * | tr -d "$sqz" | wc -c
            printf %s * | tr -dc "$sqz" | wc -c
))
chksqz '_ -'

ВИХІД

#There are 101 files in this test directory.
#All filenames combined contain a total of 17744 bytes.
#Of which 2692 bytes are not '_ -' and 15052 bytes are '_ -'.
#And to confirm these figures:
#Total: 17744
#Other: 2692
#'_ -': 15052

Добре. Тепер, нарешті, до дії:

ifsqz '_ -'
chksqz '_ -'

ВИХІД

#There are 101 files in this test directory.
#All filenames combined contain a total of 2692 bytes.
#Of which 2692 bytes are not '_ -' and 0 bytes are '_ -'.
#And to confirm these figures:
#Total: 2692
#Other: 2692
#'_ -': 0

Успіху! Ви самі можете переконатися:

ls

????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????
??????????????????????
????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
??????????????????????????
????????????????????????
????????????????????
??????????????????
????????????????????????????
??
????????????????????????????
??????????????????????????
????????????????????????????
????????????????????????????
????????????????????!!""##
??????????????????!!""##$$
????????????????!!""##$$%%
????????????!!""##$$%%&&((
????????!!""??##$$%%&&(())
$$%%&&(())**??++,,..0011
%%&&(())**++??,,..00112233
&&(())**++,,??..0011223344
))**++,,..??0011223344556
**++,,..00??11223344556677
22334455667788??99::;;<<==>>
445566778899??::;;<<==>>??@@
5566778899::;;??<<==>>??@@AA
6778899::;;<<??==>>??@@AABB
8899::;;<<==??>>??@@AABBCCDD
\\]]^^``aa??bbccddeeffgghh
]]^^``aabbc??cddeeffgghhii
^^``aabbccdd??eeffgghhiijj
??@@AABBCCDDEE??FFGGHHIIJJKK
AABBCCDDEEFF??GGHHIIJJKKLLM
BBCCDDEEFFGG??HHIIJJKKLLMMNN
CCDDEEFFGGHHII??JJKKLLMMNNOO
EEFFGGHHIIJJ??KKLLMMNNOOPPQQ
ffgghhiijjkk??llmmnnooppqqrr
gghhiijjkkllmm??nnooppqqrrss
iijjkkllmmnn??ooppqqrrsstt
jjkkllmmnnoo??ppqqrrssttuuvv
kkllmmnnooppqq??rrssttuuvvww
LLMMNNOOPPQQRR??SSTTUUVVWWXX
MNNOOPPQQRRSS??TTUUVVWWXXYY
OOPPQQRRSSTT??UUVVWWXXYYZZ[[
PPQQRRSSTTUUVV??WWXXYYZZ[[\\
RRSSTTUUVVWW??XXYYZZ[[\\]]
ssttuuvvwwxx??yyzz{{||}}~~??
ttuuvvwwxxyyz??z{{||}}~~????
uuvvwwxxyyzz{{??||}}~~??????
wwxxyyzz{{||??}}~~??????????
xxyyzz{{||}}~~??????????????
YYZZ[[\\]]^^??``aabbccddee
ZZ[[\\]]^^``??aabbccddeeff

2
+1 для творчого використання IFS+printf
— John1024

@ John1024 - що насправді цікаво:set -- 'some arbitrary' args; eval printf '"%s\n"' "$(IFS=0; printf ' "$@" %s' $(printf %025d))"
— mikeserv

1
new="$(IFS=" -_"; printf %s $1)"forks subhell (за винятком ksh93) і має проблеми з підведенням нових рядків. Ще один варіант - використовувати IFS=' -_'; set -- $1; IFS=; new="$*"(і змінити цикл while на цикл на for)
— Stéphane Chazelas

1
[ -e x ]поверне помилковим, якщо xє символьним посиланням на неіснуючий або недоступний файл.
— Стефан Шазелас

1
Гарна оболонка Кунг-Фу!
— контрмодель

2

якщо у вас є perl, ви зазвичай перейменовуєте. Ви можете зробити:

> type rename
rename is /usr/bin/rename

і покажіть, як написано цей сценарій:

> cat /usr/bin/rename | head -n 5 #firt 5 lines for example
#!/usr/bin/perl -w
#
#  This script was developed by Robin Barker (Robin.Barker@npl.co.uk),
#  from Larry Wall's original script eg/rename from the perl source.
#

Цей скрипт не підтримує прапор -i (це версія в моїй системі), але, можливо, ваша підтримка. Як щодо аргументів. По-перше, це регулярні вирази у форматі PCRE, вони працюють як фільтр, змінюють ім'я вводу на ім'я виводу. Список вхідних імен, які ви даєте зірочкою '*'. наприклад, ви робите:

> cd /tmp
> rename 's/ //g' *

в реальному "*" можна розширити на:

> rename 's/ //g' file1 file2 file3 othe files found in current directory

Коли у вас дійсно великі файли для підрахунку, ви потрапляєте в пастку. оболонка розширить вашу лінійку довше, ніж приймає система. то ви можете зробити обхід, використовуючи find або xargs. використання 'find' є проблемою, оскільки перейменування буде називатися багато разів рівним кількості файлів у каталозі. краще використовувати xargs з опцією -r. один виклик перейменування змінює багато файлів. наприклад:

> ls | xargs -r rename 's/ //g'   #thats all, names will be appended at the end of this command.

Остання проблема, що це означає:

's/ //g'

це регулярний вираз для зміни імен. після першого '/' - пробіл. це виявляється і замінюється рядком після секунди '/'. Але є порожня рядок, що закінчується третьою '/', тоді пробіл замінюється нічим. Варіант 'g' робить цей вираз повторюваним. Вираз буде ходити за всіма іменами від початку до кінця та виявляти всі пробіли.

Але що робити, якщо у вас є символ вкладки або інший "білий" символ? є заміна цього "\ s". які ще непотрібні символи? просто додайте його до вираження. Всі закрити дужками, наприклад:

's/[\s_-]//g'

це все. ти бачиш схожість? Я думаю, ви повинні прочитати man perlrequick і man perlretut, це пояснить вам (сподіваюся), як працює регулярний вираз. ви можете використовувати команду перейменування у власному сценарії, якщо вона потрібна.


1

Наступний shцикл оболонки видалить усі пробіли, підкреслення та тире з імен файлів у поточному каталозі, обережно не перезаписуючи жодних існуючих файлів:

for f in *; do
    test -f "$f" || continue
    nf=$( echo "$f" | tr -d ' _-' )
    ! test -e "$nf" && echo mv "$f" "$nf"
done

Бо bashі ksh, будучи трохи більш детальним з логікою:

for f in *; do
    if [[ -f "$f" ]]; then
        nf=$( tr -d ' _-' <<<"$f" )
        if [[ ! -e "$nf" ]]; then
            echo mv "$f" "$nf"
        fi
    fi
done

Видаліть, echoколи ви впевнені, він робить те, що ви хочете.

trКоманда видалить ( -d) будь-який символ в заданому наборі символів ( ' _-'). Важливо мати тире в самому початку або в кінці набору, інакше це буде інтерпретуватися як діапазон символів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.