Как заменить пробелы в именах файлов с помощью скрипта bash
может ли кто-нибудь рекомендовать безопасное решение для рекурсивной замены пробелов подчеркиванием в именах файлов и каталогов, начиная с заданного корневого каталога? Например:
$ 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 ответов:
использовать
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 rmb1"
/usr/bin/rename(скрипт на Perl):find /tmp/ -depth -name "* *" -execdir rename 's/ /_/g' "{}" \;
Я использую:
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 .
find . -depth -name '* *' \ | while IFS= read -r f ; do mv -i "$f" "$(dirname "$f")/$(basename "$f"|tr ' ' _)" ; doneсначала не получилось, потому что я не думал о каталогах.
A найти/переименуйте решение. переименовать - это часть пакета util-Linux-систем.
сначала нужно спуститься на глубину, потому что имя файла пробела может быть частью каталога пробела:
find /tmp/ -depth -name "* *" -execdir rename " " "_" "{}" ";"
вы можете использовать это:
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
вот (довольно подробное) решение find-exec, которое записывает предупреждения" file already exists " в stderr:
function trspace() { declare dir name bname dname newname replace_char [ $# -lt 1 -o $# -gt 2 ] && { echo "usage: trspace dir char"; return 1; } dir="" 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:]]/}" if [[ -e "${newname}" ]]; then echo "Warning: file already exists: ${newname}" 1>&2 else mv "${name}" "${newname}" fi done ' "${replace_char}" '{}' + } trspace rootdir _
это делает немного больше. Я использую его для переименования загруженных торрентов (без специальных символов (не 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 [0-7][_]; tr [0-][_]; # 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,$_); } }
Я нашел вокруг этого сценария, это может быть интересно :)
IFS=$'\n';for f in `find .`; do file=$(echo $f | tr [:blank:] '_'); [ -e $f ] && [ ! -e $file ] && mv "$f" $file;done;unset IFS
вот решение скрипта bash разумного размера
#!/bin/bash ( IFS=$'\n' for y in $(ls ) do mv /`echo $y | sed 's/ /\ /g'` /`echo "$y" | sed 's/ /_/g'` done )
рекурсивная версия ответов Найдима.
find . -name "* *" | awk '{ print length, }' | sort -nr -s | cut -d" " -f2- | while read f; do base=$(basename "$f"); newbase="${base// /_}"; mv "$(dirname "$f")/$(basename "$f")" "$(dirname "$f")/$newbase"; done
только находит файлы внутри текущего каталога и переименовывает их. У меня есть этот псевдоним.
find ./ -name "* *" -type f -d 1 | perl -ple '$file = $_; $file =~ s/\s+/_/g; rename($_, $file);
Я просто сделать один для моей собственной цели. Вы можете использовать его в качестве ссылки.
#!/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
для файлов в папке с именем / 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
для тех, кто борется через это с помощью macOS, сначала установите все инструменты:
brew install tree findutils renameзатем, когда необходимо переименовать, сделайте псевдоним для GNU find (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
Comments