Как заменить пробелы в именах файлов с помощью bash-скрипта

182

Может ли кто-нибудь рекомендовать безопасное решение для рекурсивной замены пробелов с символами подчеркивания в именах файлов и каталогов, начиная с заданного корневого каталога? Например:

$ tree
.
|-- a dir
|   `-- file with spaces.txt
`-- b dir
    |-- another file with spaces.txt
    `-- yet another file with spaces.pdf

становится:

$ tree
.
|-- a_dir
|   `-- file_with_spaces.txt
`-- b_dir
    |-- another_file_with_spaces.txt
    `-- yet_another_file_with_spaces.pdf
  • 8
    Что вы хотите, чтобы произошло, если в том же каталоге есть файл с именем foo bar и другой файл с именем foo_bar ?
  • 0
    Хороший вопрос. Я не хотел бы перезаписывать существующие файлы или терять какие-либо данные. Это должно оставить это без изменений .. в идеале печать предупреждения, но это, вероятно, требует слишком много.
Показать ещё 1 комментарий
Теги:
filenames
whitespace

15 ответов

198
Лучший ответ

Используйте rename (aka prename), который является Perl script, который уже может быть в вашей системе. Сделайте это в два этапа:

find -name "* *" -type d | rename 's/ /_/g'    # do the directories first
find -name "* *" -type f | rename 's/ /_/g'

Основываясь на ответе Jürgen's и способном обрабатывать несколько уровней файлов и каталогов в одной привязке, используя версию /usr/bin/rename (Perl script):

find /tmp/ -depth -name "* *" -execdir rename 's/ /_/g' "{}" \;
  • 5
    Нет необходимости в двух шагах: используйте поиск в глубину: find dir -depth
  • 0
    @ Юрген: не с трубкой, как я показал.
Показать ещё 19 комментариев
197

Я использую:

for f in *\ *; do mv "$f" "${f// /_}"; done

Хотя он не рекурсивный, он довольно быстрый и простой. Я уверен, что кто-то здесь может обновить его, чтобы он был рекурсивным.

В части "$ {f///_}" используется механизм расширения параметра bash для замены шаблона внутри параметра с помощью поставляемой строки. Соответствующий синтаксис "$ {parameter/pattern/string}". Смотрите: https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html или http://wiki.bash-hackers.org/syntax/pe.

  • 5
    Только тот, который работал для меня. Имейте upvote!
  • 9
    Просто и работать в Mac. (Mac не имеет rename , и его слишком сложно установить с помощью brew ..)
Показать ещё 7 комментариев
83
find . -depth -name '* *' \
| while IFS= read -r f ; do mv -i "$f" "$(dirname "$f")/$(basename "$f"|tr ' ' _)" ; done

не удалось получить его правильно, потому что я не думал о каталогах.

  • 1
    Работает как шарм. Спасибо, Майкл.
  • 1
    Не работает, если в имени файла есть завершающий пробел.
Показать ещё 12 комментариев
15

вы можете использовать detox Doug Harple

detox -r <folder>
  • 0
    Просто и работает как шарм!
8

A найти/переименовать решение. переименовать является частью util-linux.

Сначала нужно спуститься по глубине, потому что имя файла пробела может быть частью каталога пробелов:

find /tmp/ -depth -name "* *" -execdir rename " " "_" "{}" ";"
  • 0
    Я не получаю никаких изменений, когда я управляю твоим.
  • 0
    Проверьте настройку util-linux: $ rename --version rename (util-linux-of 2.17.2)
Показать ещё 4 комментария
5

вы можете использовать это:

    find . -name '* *' | while read fname 

do
        new_fname=`echo $fname | tr " " "_"`

        if [ -e $new_fname ]
        then
                echo "File $new_fname already exists. Not replacing $fname"
        else
                echo "Creating new file $new_fname to replace $fname"
                mv "$fname" $new_fname
        fi
done
4

bash 4.0

#!/bin/bash
shopt -s globstar
for file in **/*\ *
do 
    mv "$file" "${file// /_}"       
done
  • 0
    Похоже, что это сделает mv для себя, если имя файла или каталога не имеет места в нем (mv: не может переместить a' to a subdirectory of itself, a / a»)
  • 0
    не имеет значения просто удалите сообщение об ошибке, перенаправив в /dev/null .
Показать ещё 11 комментариев
2

Это делает немного больше. Я использую его для переименования загруженных торрентов (никаких специальных символов (не ASCII), пробелов, нескольких точек и т.д.).

#!/usr/bin/perl

&rena(`find . -type d`);
&rena(`find . -type f`);

sub rena
{
    ($elems)=@_;
    @t=split /\n/,$elems;

    for $e (@t)
    {
    $_=$e;
    # remove ./ of find
    s/^\.\///;
    # non ascii transliterate
    tr [\200-\377][_];
    tr [\000-\40][_];
    # special characters we do not want in paths
    s/[ \-\,\;\?\+\'\"\!\[\]\(\)\@\#]/_/g;
    # multiple dots except for extension
    while (/\..*\./)
    {
        s/\./_/;
    }
    # only one _ consecutive
    s/_+/_/g;
    next if ($_ eq $e ) or ("./$_" eq $e);
    print "$e -> $_\n";
    rename ($e,$_);
    }
}
2

Здесь (довольно многословное) решение find -exec, которое пишет "файл уже существует", предупреждает stderr:

function trspace() {
   declare dir name bname dname newname replace_char
   [ $# -lt 1 -o $# -gt 2 ] && { echo "usage: trspace dir char"; return 1; }
   dir="${1}"
   replace_char="${2:-_}"
   find "${dir}" -xdev -depth -name $'*[ \t\r\n\v\f]*' -exec bash -c '
      for ((i=1; i<=$#; i++)); do
         name="${@:i:1}"
         dname="${name%/*}"
         bname="${name##*/}"
         newname="${dname}/${bname//[[:space:]]/${0}}"
         if [[ -e "${newname}" ]]; then
            echo "Warning: file already exists: ${newname}" 1>&2
         else
            mv "${name}" "${newname}"
         fi
      done
  ' "${replace_char}" '{}' +
}

trspace rootdir _
1

Я нашел вокруг этого script, это может быть интересно:)

 IFS=$'\n';for f in `find .`; do file=$(echo $f | tr [:blank:] '_'); [ -e $f ] && [ ! -e $file ] && mv "$f" $file;done;unset IFS
  • 0
    Сбой файлов с символами новой строки в их имени.
1

Здесь разумное решение bash script

#!/bin/bash
(
IFS=$'\n'
    for y in $(ls $1)
      do
         mv $1/`echo $y | sed 's/ /\\ /g'` $1/`echo "$y" | sed 's/ /_/g'`
      done
)
0

для тех, кто борется с этим с помощью macOS, сначала установите все инструменты:

 brew install tree findutils rename

а затем, когда необходимо переименовать, создайте псевдоним для поиска GNU (gfind) в качестве find, затем запустите строку @Michel Krelin:,

alias find=gfind 
find . -depth -name '* *' \
| while IFS= read -r f ; do mv -i "$f" "$(dirname "$f")/$(basename "$f"|tr ' ' _)" ; done   
0

Для файлов в папке с именем /files

for i in `IFS="";find /files -name *\ *`
do
   echo $i
done > /tmp/list


while read line
do
   mv "$line" `echo $line | sed 's/ /_/g'`
done < /tmp/list

rm /tmp/list
0

Я просто делаю это ради своей цели. Вы можете использовать его как ссылку.

#!/bin/bash
cd /vzwhome/c0cheh1/dev_source/UB_14_8
for file in *
do
    echo $file
    cd "/vzwhome/c0cheh1/dev_source/UB_14_8/$file/Configuration/$file"
    echo "==> `pwd`"
    for subfile in *\ *; do [ -d "$subfile" ] && ( mv "$subfile" "$(echo $subfile | sed -e 's/ /_/g')" ); done
    ls
    cd /vzwhome/c0cheh1/dev_source/UB_14_8
done
0

Только находит файлы внутри текущего каталога и переименовывает их. У меня это aliased.

find ./ -name "* *" -type f -d 1 | perl -ple '$file = $_; $file =~ s/\s+/_/g; rename($_, $file);

Ещё вопросы

Сообщество Overcoder
Наверх
Меню