Как заменить пробелы в именах файлов с помощью bash script
Может ли кто-нибудь рекомендовать безопасное решение для рекурсивной замены пробелов с символами подчеркивания в именах файлов и каталогов, начиная с заданного корневого каталога? Например:
$ 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
Ответы
Ответ 1
Используйте 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' "{}" \;
Ответ 2
Я использую:
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.
Ответ 3
find . -depth -name '* *' \
| while IFS= read -r f ; do mv -i "$f" "$(dirname "$f")/$(basename "$f"|tr ' ' _)" ; done
не удалось получить его правильно, потому что я не думал о каталогах.
Ответ 4
вы можете использовать detox
Doug Harple
detox -r <folder>
Ответ 5
A найти/переименовать решение. переименовать является частью util-linux.
Сначала нужно спуститься по глубине, потому что имя файла пробела может быть частью каталога пробелов:
find /tmp/ -depth -name "* *" -execdir rename " " "_" "{}" ";"
Ответ 6
bash 4.0
#!/bin/bash
shopt -s globstar
for file in **/*\ *
do
mv "$file" "${file// /_}"
done
Ответ 7
вы можете использовать это:
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
Ответ 8
Здесь (довольно многословное) решение 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 _
Ответ 9
Это делает немного больше. Я использую его для переименования загруженных торрентов (никаких специальных символов (не ASCII), пробелов, нескольких точек и т.д.).
#!/usr/bin/perl
&rena(`find . -type d`);
&rena(`find . -type f`);
sub rena
{
($elems)[email protected]_;
@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,$_);
}
}
Ответ 10
Здесь разумное решение 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
)
Ответ 11
Я нашел вокруг этого script, это может быть интересно:)
IFS=$'\n';for f in `find .`; do file=$(echo $f | tr [:blank:] '_'); [ -e $f ] && [ ! -e $file ] && mv "$f" $file;done;unset IFS
Ответ 12
Рекурсивная версия 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
Ответ 13
Только находит файлы внутри текущего каталога и переименовывает их. У меня это aliased.
find ./ -name "* *" -type f -d 1 | perl -ple '$file = $_; $file =~ s/\s+/_/g; rename($_, $file);
Ответ 14
Я просто делаю это ради своей цели.
Вы можете использовать его как ссылку.
#!/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
Ответ 15
Для файлов в папке с именем /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
Ответ 16
Для тех, кто борется с этим с помощью macOS, сначала установите все инструменты:
brew install tree findutils rename
Затем, когда необходимо переименовать, создайте псевдоним для GNU find (gfind) as find. Затем запустите код @Michel Krelin:
alias find=gfind
find . -depth -name '* *' \
| while IFS= read -r f ; do mv -i "$f" "$(dirname "$f")/$(basename "$f"|tr ' ' _)" ; done
Ответ 17
Даст 'Детокс' попробовать.
Проблема действительно в том, например, в скобках в именах папок и файлов (без пробелов) и в избавлении от целой связки этих скобок во всех именах папок, вложенных именах и именах файлов проекта.
Борьба с bash grep и sed в этом случае. Трудно найти один простой, уникальный ответ.