Bir bash betiği kullanarak dosya adlarındaki boşluklar nasıl değiştirilir


264

Herkes belirli bir kök dizinden başlayarak boşlukları dosya ve dizin adlarında alt çizgi ile tekrar tekrar değiştirmek için güvenli bir çözüm önerebilir mi? Örneğin:

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

dönüşür:

$ tree
.
|-- a_dir
|   `-- file_with_spaces.txt
`-- b_dir
    |-- another_file_with_spaces.txt
    `-- yet_another_file_with_spaces.pdf

9
Aynı dizinde bir dosya foo barve başka bir dosya çağrılırsa ne olmak istersiniz foo_bar?
Mark Byers

İyi soru. Mevcut dosyaların üzerine yazmak veya veri kaybetmek istemem. Bunu değiştirmeden bırakmalı .. ideal olarak bir uyarı yazıyor ama bu muhtemelen çok fazla soruyor.
armandino

Yanıtlar:


312

Zaten sisteminizde olabilecek bir Perl betiği olan rename(aka prename) kullanın . İki adımda yapın:

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

Dayanarak Jürgen en cevabı ve "Revision 1.5 1998/12/18 16:16:31 RMB1" versiyonu kullanılarak tek taraflı da dosya ve dizinleri çok katmanlı üstesinden /usr/bin/rename(bir Perl komut dosyası):

find /tmp/ -depth -name "* *" -execdir rename 's/ /_/g' "{}" \;

5
İki adıma gerek yok: İlk derinlik aramasını kullanın: dir
-depth'i

3
Oh, sadece okudum renamemanpage (Ben aracı bilmiyordum) ve sana değiştirerek kodunuzu optimize düşünüyorum s/ /_/giçin y/ /_/;-)
korsanı - Michael Krelin

1
Elbette bundan bir performans artışı almayacaksınız. Daha çok doğru aracı kullanmakla ilgili. Ve tüm bu soru az çok mikro optimizasyon ile ilgilidir. Ne de olsa eğlenceli değil mi? ;-)
Michael Krelin - hacker

14
Bunu OS X'te çalıştırıyorsanız, şunları yapmanız gerekirbrew install rename
loeschg

7
Bu, rentos komutu tamamen farklı olduğundan (perl betiği değil, bir ikili dosyadır) ve stdin'den veri kabul etmediğinden, Centos 7'de çalışmaz.
CpnCrunch

357

Kullanırım:

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

Yinelemeli olmasa da, oldukça hızlı ve basit. Eminim ki burada birisi tekrarlamalı olarak güncelleyebilir.

${f// /_}Bölüm sağlanan dize ile bir parametre içinde bir desen yerine bash parametre genişletme mekanizmasını kullanır. İlgili sözdizimi ${parameter/pattern/string}. Bkz . Https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html veya http://wiki.bash-hackers.org/syntax/pe .


9
Basit ve Mac'te çalışır. (mac yok rename, ve bunu demlemek ile kurmak çok zor ..)
JonnieJS

6
harika cevap. for d in *\ *; do mv "$d" "${d// /}"; donepuan altında olmayan kullandım .
Yoon Lee

1
'Bul-adı' cevabının aksine, bu soru OS X üzerinde çalıştı! Teşekkürler bayım!
Julio Faerman

1
Referans için, bu kolayca yapabilirsiniz haline kullanma bash özyinelemeli shopt -s globstarve for f in **/*\ *; do .... Bu globstarseçenek bash'ın içindedir, ancak renamekomut bash'ın bir parçası değil, ortak bir Linux aracıdır.
ghoti

2
${f// /_}arama ve değiştirme için bir Bash değişken genişletmesidir . - Boşluk içeren her dosya için döngüdeki değişkendir . - Birincisi "hepsini değiştir" anlamına gelir (ilk seferinde durmayın). - Daha sonra `/ _`," alanı alt çizgiyle değiştir "anlamına gelirffor//
Ari

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

ilk başta doğru yapamadı, çünkü dizinleri düşünmemiştim.


1
Tıkır tıkır çalışıyor. Teşekkürler Michael.
armandino

Dennis, iyi yakalama, IFS=''önüne koyarak kolayca sabitlenir read. Ayrıca, diğer yorumlarla söyleyebileceğim için, sortadım -depthseçeneği lehine bırakılabilir find.
Michael Krelin - hacker

1
Bir dosya adı \ (ters eğik çizgi) içeriyorsa çalışmaz . -rOkuma seçeneği eklenerek düzeltilebilir .
jfg956

8
Çözümünüzü kopyalamak ve kullanmak için bu sayfayı ziyaret ettiğimde bu 50. kez olmalıdır. Teşekkürler çok . Mac'te olduğum ve renameDennis'in önerdiği komuta sahip olmadığım için cevabınızı tercih ederim .
Alex Constantin

@AlexConstantin, yok macportsvar rename? Ben asla öğrenmek için uğraşmadı çünkü görevin yarar haklı olduğunu sanmıyorum. Ve yoksa macports, bunları yüklemeyi düşünmelisiniz;)
Michael Krelin - hacker

30

detoxDoug Harple tarafından kullanabilirsiniz

detox -r <folder>

12

Bir bulmak / yeniden adlandırma çözüm. rename util-linux'un bir parçasıdır.

Bir boşluk dosya adı bir boşluk dizininin parçası olabileceğinden, önce derinliği azaltmanız gerekir:

find /tmp/ -depth -name "* *" -execdir rename " " "_" "{}" ";"

Seninkini çalıştırdığımda hiçbir değişiklik yapmıyorum.
sonraki duyuruya kadar duraklatıldı.

Util-linux kurulumunu kontrol edin: $ rename --version rename (util-linux-ng 2.17.2)
Jürgen Hötzel

/ Usr / bin / rename (bir Perl betiği), "Revizyon 1.5 1998/12/18 16:16:31 rmb1" değerini gösterir
sonraki duyuruya kadar duraklatıldı.

Hmm ... util-linux ikili dosyalarınız nereye gitti? Bu dosya yolu util-linux'a ait olmalıdır. Bize GNU-Linux sistemi değil misiniz?
Jürgen Hötzel

1
ki bu benim koşumda sadece bir alanı değiştirir , bu yüzden "git dağda ateş et" "dağda go_tell ateşi" olur.
brokkr

6

bash 4.0

#!/bin/bash
shopt -s globstar
for file in **/*\ *
do 
    mv "$file" "${file// /_}"       
done

Görünüşe göre bir dosya veya dizin adında boşluk yoksa kendi kendine bir mv yapacak (mv: a' to a subdirectory of itself, a / a ' hareket edemiyor )
armandino

önemli değil. adresine yönlendirerek hata mesajını kaldırmanız yeterlidir /dev/null.
ghostdog74

ghostdog, mvsadece dört dosyayı yeniden adlandırmak için elli beş bin kez yumurtlama, iletileri mesajla doldurmasanız bile biraz ek yük olabilir.
Michael Krelin - hacker

krelin, find bile boşluklu olanları bulmak için bahsettiğiniz 55000 dosyadan geçecek ve daha sonra yeniden adlandıracak. Arka uçta, hala her şeyden geçiyor. İsterseniz, yeniden adlandırma işleminden önce boşluk olup olmadığını ilk kez kontrol edin.
ghostdog74

Ben geçmiyorum, mv yumurtlama hakkında konuşuyordum. Daha for file in *' '*iyi bir iş yapmaz mıydı, bazıları mı?
Michael Krelin - hacker

6

bunu kullanabilirsiniz:

    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

3

Naidim'in Yanıtlarının özyinelemeli sürümü.

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

İşte stderr'e "dosya zaten var" uyarıları yazan (oldukça ayrıntılı) bir find -exec çözümü:

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 _

2

Bu biraz daha fazlasını yapıyor. İndirilmiş torrentlerimi (özel karakterler (ASCII olmayan), boşluklar, çoklu noktalar vb.) Yeniden adlandırmak için kullanıyorum.

#!/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,$_);
    }
}

1

Bu senaryoyu buldum, ilginç olabilir :)

 IFS=$'\n';for f in `find .`; do file=$(echo $f | tr [:blank:] '_'); [ -e $f ] && [ ! -e $file ] && mv "$f" $file;done;unset IFS

Adlarında yeni satır bulunan dosyalarda başarısız olur.
ghoti


1

MacOS kullanarak bu sorunla mücadele edenler için, önce tüm araçları yükleyin:

 brew install tree findutils rename

Daha sonra yeniden adlandırılması gerektiğinde, GNU find (gfind) için find olarak bir takma ad oluşturun. Sonra @Michel Krelin kodunu çalıştırın:

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

0

Bu yalnızca geçerli dizinin içindeki dosyaları bulur ve yeniden adlandırır . Bunu taklit ettim.

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


0

Sadece kendi amacım için bir tane yapıyorum. Referans olarak kullanabilirsiniz.

#!/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

/ Files adlı klasördeki dosyalar için

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

Soruna benim çözüm bash betiği:

#!/bin/bash
directory=$1
cd "$directory"
while [ "$(find ./ -regex '.* .*' | wc -l)" -gt 0 ];
do filename="$(find ./ -regex '.* .*' | head -n 1)"
mv "$filename" "$(echo "$filename" | sed 's|'" "'|_|g')"
done

betiği uygulamak istediğiniz dizin adını betiği yürüttükten sonra bir bağımsız değişken olarak koymanız yeterlidir.


0

MacOS'ta

Tıpkı seçilen cevap gibi.

brew install rename

# 
cd <your dir>
find . -name "* *" -type d | rename 's/ /_/g'    # do the directories first
find . -name "* *" -type f | rename 's/ /_/g'
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.