Dizini Düzleştir ama Dizin Adlarını Yeni Dosya Adında Koru


11

Bir dizini aşağıdaki biçimde nasıl düzleştirebilirim?

Önce: ./aaa/bbb/ccc.png

Sonra: ./aaa-bbb-ccc.png


Not: Lütfen tek karakterli dosya adlarını da (ör. Boşluk) dikkate alın
Nathan JB

3
Ayrıca, her ikisinin de mevcut olduğu ./foo/bar/baz.pngve ./foo-bar-baz.pngher ikisinin de olası durumunu ele almak istediğinizi belirtmeniz gerekir . Sanırım ikincisini öncekiyle değiştirmek istemiyor musunuz?
jw013

Benim durumumda, bu alakasız, ama cevap gerçekten bunu açıklamalı ki diğerleri buna güvenebilsin! Teşekkürler!
Nathan JB

Yanıtlar:


7

Uyarı: Bu komutların çoğunu doğrudan tarayıcımda yazdım. Uyarıcı lector.

Zsh ve zmv ile :

zmv -o -i -Qn '(**/)(*)(D)' '${1//\//-}$2'

Açıklama: Kalıp **/*, geçerli dizinin alt dizinlerindeki tüm dosyalarla özyinelemeli olarak eşleşir (geçerli dizindeki dosyalarla eşleşmez, ancak bunların yeniden adlandırılması gerekmez). İlk iki parantez çifti , değiştirilen metin olarak $1ve $2değiştirilebilecek gruplardır . Son parantez çifti nokta D niteleyicisini ekler, böylece nokta dosyaları atlanmaz. mevcut bir dosyanın üzerine yazılıp yazılmayacağı sorulursa seçeneği -o -iiletmek anlamına gelir .-imv


Yalnızca POSIX araçlarıyla:

find . -depth -exec sh -c '
    for source; do
      case $source in ./*/*)
        target="$(printf %sz "${source#./}" | tr / -)";
        mv -i -- "$source" "${target%z}";;
      esac
    done
' _ {} +

Explanation: casedeyimi, geçerli dizini ve geçerli dizinin üst düzey alt dizinlerini atlar. satır başıyla sıyrılan ve tüm eğik çizgiler yerine tire işareti targetbulunan bir kaynak dosya adı ( $0) ./ve bir son içerir z. Son z, dosya adının yeni bir satırla bitmesi durumunda vardır: aksi takdirde komut ikamesi onu soyurur.

Eğer finddesteklemiyorsa -exec … +(OpenBSD, sana bakıyorum):

find . -depth -exec sh -c '
    case $0 in ./*/*)
      target="$(printf %sz "${0#./}" | tr / -)";
      mv -i -- "$0" "${target%z}";;
    esac
' {} \;

Bash (veya ksh93) ile, eğik çizgileri tire işareti ile değiştirmek için harici bir komut çağırmanıza gerek yoktur, ksh93 parametre genişletmesini dize değiştirme yapısıyla kullanabilirsiniz ${VAR//STRING/REPLACEMENT}:

find . -depth -exec bash -c '
    for source; do
      case $source in ./*/*)
        source=${source#./}
        target="${source//\//-}";
        mv -i -- "$source" "$target";;
      esac
    done
' _ {} +

for sourceÖrnekler Sonunda sen (normalde) kullandık neden buldum ... ilk sunulan dosya / dir kaçırma _olarak $0:) ... ve teşekkür harika yanıtlar için. Onlardan çok şey öğreniyorum.
Peter.O

Zmv örneğinin sadece kuru bir çalışma yaptığını unutmayın: move komutlarını yazdırır, ancak bunları yürütmez. Bu komutları gerçekten çalıştırmak için -Qn içindeki n'yi kaldırın.
Peter

5

Burada zaten iyi cevaplar olsa da, bunu daha sezgisel buluyorum bash:

find aaa -type f -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); mv "{}" "$new"' \;

aaaMevcut dizininiz nerede . İçerdiği dosyalar geçerli dizine taşınır (aaa içinde yalnızca boş dizinler bırakılır). İki trdizin ve boşlukları ele almak için iki çağrı (kaçan eğik çizgiler için mantık olmadan - okuyucu için bir egzersiz).

Bunu daha sezgisel ve değiştirmesi nispeten kolay görüyorum. Yani findsadece .png dosyalarını bulmak istediğimi söylersem parametreleri değiştirebilirim . trAramaları değiştirebilir veya gerekirse daha fazlasını ekleyebilirim. Ve görsel olarak doğru şeyi yapacağını kontrol etmek istersem daha echoönce ekleyebilirim mv(daha sonra echosadece şeyler doğru görünüyorsa ekstra olmadan tekrarlayın :

find aaa -name \*.png -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); echo mv "{}" "$new"' \;

Ayrıca ikincisini de son dosya adında komik eserler bırakacağı için ... find aaadeğil find .... kullanıyorum find aaa/.


Bu bir astar için teşekkürler - bu dizinin dışından (tam olarak ne dediğini) dışarı yaptım bu benim için iyi çalıştı. Dizin içinden yapmaya çalıştığımda (kök dizin adının eklenmesini önlemek umuduyla) tüm dosyalar "kayboldu". Onları aynı dizindeki gizli dosyalara dönüştürmüştüm.
dgig

2
find . -mindepth 2 -type f -name '*' |
  perl -l000ne 'print $_;  s/\//-/g; s/^\.-/.\// and print' |
    xargs -0n2 mv 

Not: bu, içerdiği dosya adı için çalışmaz \n.
Bu, elbette sadece tip fdosyalarını taşır ...
Tek isim çakışması, pwd'de önceden mevcut olan dosyalardan olabilir.

Bu temel alt kümeyle test edildi

rm -fr junk
rm -f  junk*hello*

mkdir -p  junk/junkier/junkiest
touch    'hello    hello'
touch    'junk/hello    hello'
touch    'junk/junkier/hello    hello'
touch    'junk/junkier/junkiest/hello    hello'

Sonuçlanan

./hello    hello
./junk-hello    hello
./junk-junkier-hello    hello
./junk-junkier-junkiest-hello    hello

0

İşte bir lssürüm .. yorum hoş geldiniz. Bu gibi saçma dosya adları için çalışır "j1ळ\n\001\n/j2/j3/j4/\nh  h\n", ancak herhangi bir tuzak bilmek gerçekten ilgi duyuyorum. Bu daha çok "kötü huylu lsgerçekte yapabilir mi (sağlamca)?"

ls -AbQR1p * |                        # paths in "quoted" \escaped format
    sed -n '/":$/,${/.[^/]$/p}' |     # skip files in pwd, blank and dir/ lines
    { bwd="$PWD"; while read -n1;     # save base dir strip leading "quote
                        read -r p; do # read path
        printf -vs "${p%\"*}"         # strip trailing quote"(:)
        [[ $p == *: ]] && {           # this is a directory
            cd   "$bwd/$s"            # change into new directory
            rnp="${s//\//-}"-         # relative name prefix
        } || mv "$s" "$bwd/$rnp$s"
      done; }

-1

Sadece trkomut için dosya adını + yolunu ekleyin.tr <replace> <with>

for FILE in `find aaa -name "*.png"`
do
  NEWFILE=`echo $FILE | tr '/' '-'`
  cp -v $FILE $NEWFILE
done

1
Bu, garip dosya adlarını güvenli bir şekilde işlemez. Uzaylar onu kıracak (diğer şeylerin yanı sıra).
jw013

İlk bakışta, bu temiz, okunabilir bir çözümdür, ancak @ jw013 doğrudur, boşlukları iyi işlemez. Değişen misiniz cphattı cp -v "$FILE" "$NEWFILE"yardım? (Ayrıca, yalnızca png dosyalarını varsaymamalısınız.)
Nathan JB

2
@ NathanJ.Brauer Satıra tırnak eklemek cpyetersiz. findÇıktıyı bir fordöngü ile ayrıştırma yaklaşımının tamamı kusurludur. Kullanımına tek güvenli yolu findile olan -execveya -print0(daha az taşınabilir varsa -exec). Daha sağlam bir alternatif öneririm, ancak sorunuz şu anda yeterince belirtilmemiş.
jw013

-2

Dosya adlarındaki alanlar için güvenlidir:

#!/bin/bash

/bin/find $PWD -type f | while read FILE
do
    _new="${FILE//\//-}"
    _new="${_new:1}"
    #echo $FILE
    #echo $_new

    /bin/mv "$FILE" "$_new"
done

İle mayın Ran cpyerine mvbariz nedenlerle ancak aynı üretmelidir.


3
type find-> find is /usr/bin/findmakinemde. PATHBir komut dosyasında değişken adı olarak kullanmak korkunç bir fikirdir. Ayrıca, komut dosyanız yanlış kullanıldığından read(bu sitede arama yapın) komut dosyanız dosya adlarını boşluk veya ters eğik çizgilerle yönetir IFS read -r.
Gilles 'SO- kötü olmayı kes'

find is hashed (/bin/find), ilgili nasıl? Farklı dağıtımlar farklı mı?
Tim

Bundan uzak durmam gerektiğini biliyordum, akbaba yem.
Tim

@Tim Temsilcinizi geri almak için cevabı her zaman silebilirsiniz (ve benim de b / c'nin indirilmesi için temsilciye mal olması gerekir). Komut dosyalarındaki sabit kodlama yolları PATH, her Unix sisteminin kullandığı bir şey olan ortam değişkeninin noktasını kaçırdığınız anlamına gelir . Eğer yazarsanız /bin/find, komut dosyanız aslında olmayan her sistemde (Gilles, mayın ve muhtemelen OP'ler) bozulur /bin/find(örneğin b / c içinde /usr/bin/find). PATHOrtam değişkeninin bütün mesele bunu bir sorun haline getirmektir.
jw013

Bu arada, $(pwd)bir değişkene zaten mevcuttur: $PWD. Ama gerekir değil burada mutlak bir yol kullanın: komut söz hakkından gelen çağrılır ise, /home/timbu adlandırmak girişiminde /home/tim/some/pathiçin /home-tim-some-path.
Gilles 'SO- kötü olmayı kes'
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.