Dosyaları toplu olarak yeniden adlandırmak için sed kullanma


89

Amaç

Bu dosya adlarını değiştirin:

  • F00001-0708-RG-biasliuyda
  • F00001-0708-CS-akgdlaul
  • F00001-0708-VF-hioulgigl

şu dosya adlarına:

  • F0001-0708-RG-biasliuyda
  • F0001-0708-CS-akgdlaul
  • F0001-0708-VF-hioulgigl

Kabuk Kodu

Test etmek için:

ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/'

Gerçekleştirmek:

ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/' | sh

Benim sorum

Sed kodunu anlamıyorum. İkame komutunun ne olduğunu anlıyorum

$ sed 's/something/mv'

anlamına geliyor. Ve normal ifadeleri bir şekilde anlıyorum. Ama burada ne olduğunu anlamıyorum:

\(.\).\(.*\)

Veya burada:

& \1\2/

Birincisi, bana göre, "tek bir karakter, ardından tek bir karakter, ardından tek bir karakterin herhangi bir uzunluk dizisi" anlamına geliyor - ama kesinlikle bundan daha fazlası var. İkinci kısma gelince:

& \1\2/

Hiç bir fikrim yok.


Yanıtlar:


152

Birincisi, bunu yapmanın en kolay yolunun, ilk ad veya komutları yeniden adlandırmak olduğunu söylemeliyim.

Ubuntu, OSX (Homebrew paketi rename, MacPorts paketi p5-file-rename) veya perl yeniden adlandırmalı (ön ad) diğer sistemlerde:

rename s/0000/000/ F0000*

veya RHEL gibi, util-linux-ng'den yeniden adlandırılmış sistemlerde:

rename 0000 000 F0000*

Bu, eşdeğer sed komutundan çok daha anlaşılır.

Ancak sed komutunu anlamak için, sed manpage yardımcı olur. Man sed'i çalıştırırsanız ve & öğesini ararsanız (aramak için / komutunu kullanarak), bunun s / foo / bar / replaceements içinde özel bir karakter olduğunu görürsünüz.

  s/regexp/replacement/
         Attempt  to match regexp against the pattern space.  If success‐
         ful,  replace  that  portion  matched  with  replacement.    The
         replacement may contain the special character & to refer to that
         portion of the pattern space  which  matched,  and  the  special
         escapes  \1  through  \9  to refer to the corresponding matching
         sub-expressions in the regexp.

Bu nedenle, \(.\)referans alınabilecek ilk karakterle eşleşir \1. Daha sonra .her zaman 0 olan bir sonraki karakterle \(.*\)eşleşir. Ardından dosya adının geri kalanıyla eşleşir ve bu karakter tarafından başvurulabilir \2.

Yerini alan dize, &(orijinal dosya adı) kullanarak hepsini bir araya getirir ve \1\2bu, 0 olan 2. karakter dışında dosya adının her bölümüdür.

Bu, bunu yapmanın oldukça şifreli bir yolu, IMHO. Herhangi bir nedenle yeniden adlandırma komutu mevcut değilse ve yeniden adlandırmak için sed'i kullanmak istediyseniz (veya belki yeniden adlandırmak için çok karmaşık bir şey yapıyordunuz?), Normal ifadenizde daha açık olmak onu çok daha okunaklı hale getirecektir. Belki şöyle bir şey:

ls F00001-0708-*|sed 's/F0000\(.*\)/mv & F000\1/' | sh

S / search / replace / içinde gerçekte neyin değiştiğini görebilmek, onu çok daha okunaklı hale getirir. Ayrıca yanlışlıkla iki kez çalıştırırsanız dosya adınızdan karakterleri emmeye devam etmez.


1
RHEL sunucumda, yeniden adlandırma sözdizimi "0000 000 F0000 * yeniden adlandır" olacaktı
David LeBauer

1
Büyük olasılıkla renamekendisi "yeniden adlandırılmış" bir bağlantıdır. yani renameedilmiştir "adını" dan prenameUbuntu, .. örneğin: readlink -f $(which rename)çıkışlar /usr/bin/prename... renamebahsettiği David tamamen farklı bir programdır.
Peter.

1
İyi nokta, Peter. Her iki yeniden adlandırma yardımcı programını ele alacak şekilde yanıtı güncelledim.
Edward Anderson

3
Bu hatayı gidermek için, boruyu sonunda sh'a çıkarın. Komutlar ekranda yankılanacaktır.
Ben Mathews

1
Rastgele verileri aktarmanın iyi bir tavsiye olduğuna emin misin sh? Bu, rastgele kod çalıştırılabildiğinden potansiyel olarak tehlikelidir (verileri kod olarak ele alıyorsunuz).
gniourf_gniourf

46

sed açıklamanız vardı, artık sadece kabuğu kullanabilirsiniz, harici komutlara gerek yok

for file in F0000*
do
    echo mv "$file" "${file/#F0000/F000}"
    # ${file/#F0000/F000} means replace the pattern that starts at beginning of string
done

1
Güzel ama parantezli referanslar yapamazsınız.
Leonidas Tsampros

28

sedBirkaç yıl önce toplu yeniden adlandırma örnekleri içeren küçük bir gönderi yazdım :

http://www.guyrutenberg.com/2009/01/12/batch-renaming-using-sed/

Örneğin:

for i in *; do
  mv "$i" "`echo $i | sed "s/regex/replace_text/"`";
done

Regex (örn grupları içeriyorsa \(subregex\) o zaman olduğu gibi yedek metninde kullanabilirsiniz \1\, \2vb


Yalnızca bağlantı yanıtlarının tavsiye edilmediğini unutmayın (bağlantılar zamanla eskimeye başlar). Lütfen cevabınızı düzenlemeyi ve buraya bir özet eklemeyi düşünün.
kleopatra

o kadar verimli değil, ancak işi birkaç yüz dosya için hallediyor. Olumlu oy verildi.
Varun Chandak

23

En kolay yol şudur:

for i in F00001*; do mv "$i" "${i/F00001/F0001}"; done

veya portatif olarak

for i in F00001*; do mv "$i" "F0001${i#F00001}"; done

Bu F00001, dosya adlarındaki öneki ile değiştirir F0001. mahesh kredisi burada: http://www.debian-administration.org/articles/150


3
Değişken enterpolasyonlarını doğru şekilde alıntı yapmalısınız; mv "$i" "${i/F00001/F0001}". Ancak +1
üçlü

7

sedkomuta

s/\(.\).\(.*\)/mv & \1\2/

değiştirmek anlamına gelir:

\(.\).\(.*\)

ile:

mv & \1\2

tıpkı normal bir sedkomut gibi . Bununla birlikte, parantezler &ve \nişaretler onu biraz değiştirir.

Arama dizesi, başlangıçtaki tek karakterle eşleşir (ve model 1 olarak hatırlar), ardından dizenin geri kalanı tarafından takip edilen tek bir karakter gelir (model 2 olarak hatırlanır).

Değiştirme dizesinde, değiştirmenin bir parçası olarak kullanmak için bu eşleşen kalıplara başvurabilirsiniz. Tüm eşleşen bölüme olarak da başvurabilirsiniz &.

Yani bu sedkomutun yaptığı şey mv, orijinal dosyaya (kaynak için) ve 1. ve 3. karaktere dayalı bir komut oluşturarak , 2. karakteri (hedef için) etkin bir şekilde kaldırmaktır. Size aşağıdaki formatta bir dizi satır verecektir:

mv F00001-0708-RG-biasliuyda F0001-0708-RG-biasliuyda
mv abcdef acdef

ve bunun gibi.


1
Bu iyi bir açıklamaydı, ancak dosyaları gerçekten yeniden adlandırmak için sed komutunu diğer komutlarla nasıl kullandığınızı belirtmek faydalı olabilir. Örneğin:ls | sed "s/\(.\).\(.*\)/mv & \1\2/" | bash
jcarballo

@jcarballo: Ayrıştırmak ls, boru içinden geçmek sedve sonra bir kabuktan geçmek tehlikelidir ! sahte dosya adlarıyla rastgele kod yürütülmesine tabidir. Sorun, verilerin veri olarak ele alınması gerektiğidir ve burada tipik olarak herhangi bir önlem alınmadan koda serileştirilir. Keşke paxdiablo gerçekten iyi bir uygulama göstermediği için bu yanıtı silebilir. (Bu soruya tökezledim çünkü | shişe yaramayan bir komutun ardından rastgele bir şekilde konuşulan ve bu soruyu gördükten sonra cevaplar daha iyi çalışacağını düşündü - dehşete düştüm!) :).
gniourf_gniourf

3

Ters eğik çizgi, "kalıbı eşleştirirken, burada eşleşenleri tutun" anlamına gelir. Daha sonra, ikame metin tarafında, hatırlanan parçaları "\ 1" (ilk parantezli blok), "\ 2" (ikinci blok) vb. İle geri alabilirsiniz.


1

Gerçekten yaptığınız tek şey, ikinci karakteri kaldırmaksa, ne olduğuna bakılmaksızın, şunu yapabilirsiniz:

s/.//2

ama sizin komutunuz bir mvkomut oluşturmak ve onu yürütmek için kabuğa iletmektir.

Bu, sizin sürümünüzden daha okunaklı değildir:

find -type f | sed -n 'h;s/.//4;x;s/^/mv /;G;s/\n/ /g;p' | sh

Dördüncü karakter kaldırılır çünkü findher dosya adının başına "./" eklenir.


Keşke bu cevabı silebilseydin. OP'nin çok özel durumunda belki iyi olsa da, bunun gibi cevapları gören ve anlamayan pek çok insan var ve | shişe yaramayacağı umuduyla rastgele bir şekilde çalışmayan bir komutun peşine düşüyor. daha iyi. Korkunç! (ve ayrıca bu iyi bir uygulama değil). Umarım anlayacaksın!
gniourf_gniourf

1

Perl yeniden adlandırmayı kullanma ( araç kutusunda olması gerekir ):

rename -n 's/0000/000/' F0000*

-nÇıktı gerçek olarak yeniden adlandırmak için iyi göründüğünde anahtarı kaldırın .

uyarı Bunu yapabilecek veya yapamayacak aynı ada sahip başka araçlar da var, bu yüzden dikkatli olun.

util-linuxPaketin parçası olan yeniden adlandırma komutu olmayacak.

Aşağıdaki komutu çalıştırırsanız ( GNU)

$ rename

ve görüyorsunuz perlexpr, o zaman bu doğru araç gibi görünüyor.

Değilse, varsayılan (genellikle zaten durum) açık Debianve aşağıdaki gibi türev yapmak için Ubuntu:

$ sudo apt install rename
$ sudo update-alternatives --set rename /usr/bin/file-rename

Archlinux için:

pacman -S perl-rename

RedHat ailesi dağıtımları için:

yum install prename

'Prename' paketi EPEL deposundadır.


Gentoo için:

emerge dev-perl/rename

* BSD için:

pkg install gprename

veya p5-File-Rename


Mac kullanıcıları için:

brew install rename

Başka bir dağıtımda bu komuta sahip değilseniz, yüklemek için paket yöneticinizi arayın veya manuel olarak yapın :

cpan -i File::Rename

Eski bağımsız sürüm burada bulunabilir


adam yeniden adlandır


Bu araç aslen Perl'in babası Larry Wall tarafından yazılmıştır.


0

Parantezler, ters eğik çizgi ile kullanılmak üzere belirli dizeleri yakalar.


0
 ls F00001-0708-*|sed 's|^F0000\(.*\)|mv & F000\1|' | bash

Korkunç! keyfi kod çalıştırmaya tabi (belki sorunun belirli bağlamında değil, ancak bunun gibi yanıtları gören ve buna benzer bir şeyi rastgele yazmaya çalışan birçok insan var ve bu çok tehlikeli!). Keşke bu yanıtı silebilseniz (ayrıca burada benim olumlu oy verdiğim başka bir iyi yanıtınız var).
gniourf_gniourf

0

İşte yapacağım şey:

for file in *.[Jj][Pp][Gg] ;do 
    echo mv -vi \"$file\" `jhead $file|
                           grep Date|
                           cut -b 16-|
                           sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done

Sonra bu iyi görünüyorsa | sh, sonuna ekleyin . Yani:

for file in *.[Jj][Pp][Gg] ;do 
    echo mv -vi \"$file\" `jhead $file|
                           grep Date|
                           cut -b 16-|
                           sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done | sh

0

Benim için işe yarayan bazı örnekler:

$ tree -L 1 -F .
.
├── A.Show.2020.1400MB.txt
└── Some Show S01E01 the Loreming.txt

0 directories, 2 files

## remove "1400MB" (I: ignore case) ...

$ for f in *; do mv 2>/dev/null -v "$f" "`echo $f | sed -r 's/.[0-9]{1,}mb//I'`"; done;
renamed 'A.Show.2020.1400MB.txt' -> 'A.Show.2020.txt'

## change "S01E01 the" to "S01E01 The"
## \U& : change (here: regex-selected) text to uppercase;
##       note also: no need here for `\1` in that regex expression

$ for f in *; do mv 2>/dev/null "$f" "`echo $f | sed -r "s/([0-9] [a-z])/\U&/"`"; done

$ tree -L 1 -F .
.
├── A.Show.2020.txt
└── Some Show S01E01 The Loreming.txt

0 directories, 2 files
$ 

-1
for i in *; do mv $i $(echo $i|sed 's/AAA/BBB/'); done

4
SO'ya hoş geldiniz. Lütfen kodunuzun açıklamasını eklemeyi düşünün. Diğer kullanıcıların bunu anlamasına yardımcı olacaktır.
Digvijay S

Bu cevap iyidir, ancak yukarıdaki oldukça olumlu oylanan bir cevabın neredeyse aynı cevabıdır.
Eric Leschinski
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.