Как заменить пробелы в именах файлов, используя скрипт bash - PullRequest
233 голосов
/ 25 апреля 2010

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

$ 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

Ответы [ 16 ]

306 голосов
/ 13 августа 2013

Я использую:

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 / синтаксис / pe .

278 голосов
/ 25 апреля 2010

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

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

Основано на ответе Юргена и может обрабатывать несколько слоев файлов и каталогов в одной строке, используя версию Revision 1.5 1998/12/18 16:16:31 /usr/bin/rename ( скрипт Perl):

find /tmp/ -depth -name "* *" -execdir rename 's/ /_/g' "{}" \;
97 голосов
/ 25 апреля 2010
find . -depth -name '* *' \
| while IFS= read -r f ; do mv -i "$f" "$(dirname "$f")/$(basename "$f"|tr ' ' _)" ; done

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

26 голосов
/ 10 января 2013

вы можете использовать detox Даг Харпл

detox -r <folder>
11 голосов
/ 25 апреля 2010

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

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

find /tmp/ -depth -name "* *" -execdir rename " " "_" "{}" ";"
6 голосов
/ 26 апреля 2010

Баш 4.0

#!/bin/bash
shopt -s globstar
for file in **/*\ *
do 
    mv "$file" "${file// /_}"       
done
5 голосов
/ 01 февраля 2013

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

    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
2 голосов
/ 10 апреля 2018

Рекурсивная версия ответов Naidim.

find . -name "* *" | awk '{ print length, $0 }' | sort -nr -s | cut -d" " -f2- | while read f; do base=$(basename "$f"); newbase="${base// /_}"; mv "$(dirname "$f")/$(basename "$f")" "$(dirname "$f")/$newbase"; done
2 голосов
/ 08 августа 2011

Этот делает немного больше. Я использую его для переименования загруженных торрентов (без специальных символов (не 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 голосов
/ 26 апреля 2010

Вот (довольно подробное) решение 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 _
...