Удалить пробелы, дефисы и подчеркивания в именах файлов?

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 для выбранных файлов.

user8547
источник
2
Я отмечаю одну проблему, которая существует во многих предлагаемых решениях, - это неправильная проверка наличия «нового» имени перед запуском файла. Невыполнение этого требования может стать источником многих проблем.
mdpc
Можно ли изменить команду John1024, чтобы проверить это?
user8547
@ user8547rename -i "s/[-_ ]//g" *
Sparhawk
Спасибо, ястреб. Кстати, для тех, кто заинтересован в использовании этого в качестве настраиваемого действия Thunar, команда для Thunar: для файла в% N; сделать mv "$ file" 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 rename.

  • Все: во- первых, проверьте, renameдоступен ли Perl под этим именем prename.

  • Debian: переименование Perl должно быть по умолчанию. Это также доступно как prename. Однако renameисполняемый файл находится под контролем /etc/alternativesи, таким образом, мог быть изменен на что-то другое.

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

  • Mac OSX Согласно этому ответу , renameможет быть установлен на OSX с помощью homebrew через:

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

     wget 'http://www.perlmonks.org/?displaytype=displaycode;node_id=303814' -O rename
John1024
источник
Я думаю, это зависит от того, о чем renameты говоришь. Тот из util-linux -2.24.2-1.fc20.x86_64 не поддерживает регулярные выражения.
Кристиан Чиупиту
1
@CristianCiupitu Я только что проверил страницу руководства для версии переименования, которую вы нашли. Исходя из аргументов, версия, renameкоторую использовал OP, выглядит как perlверсия, а не util-linuxверсия.
John1024
Для справки, это renameсправочная страница для версии util-linux . В любом случае, кроме этой записки, важно то, что ОП получил свой ответ (и вы от меня откликнулись :-D).
Кристиан Чиупиту
@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 0с

  • следующий из последнего получает 252 ''аргумента с нулевой строкой, и для каждого из них выводится содержимое, за $nкоторым следует строка" $i "

  • evalинтерпретирует аргументы следующего до того, printfкак он напечатает результаты этой интерпретации как восьмеричные числа с добавлением двух обратных косых черт

  • последний printfвыводит значения байтов для этих восьмеричных чисел 2 за раз, за ​​которым следует строка -_ ---___для каждой пары

  • $nинициализируется уравнением, которое будет увеличиваться $iна единицу для каждой оценки, за исключением того, что оно пропускает значения 10, 39 или 47 - (которые являются \newline, 'одинарными кавычками и /косой чертой в десятичном формате 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
mikeserv
источник
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)"разветвляется подоболочка (кроме ksh93) и имеет проблемы с переводом строк. Другой вариант - использовать IFS=' -_'; set -- $1; IFS=; new="$*"(и изменить цикл while на цикл for)
Стефан Шазелас
1
[ -e x ]вернет false, если 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, это объяснит вам (я надеюсь), как работает регулярное выражение. Вы можете использовать команду переименования в своем собственном скрипте, если вам это нужно.

Znik
источник
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) любой символ в данном наборе символов ( ' _-'). Важно иметь тире в самом начале или в конце набора, иначе он будет интерпретирован как диапазон символов.

Кусалананда
источник