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


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.