Vraag Hoe verander ik de extensie van meerdere bestanden recursief van de opdrachtregel?


Ik heb veel bestanden met .abc extensie en wil deze wijzigen in .edefg
Hoe dit te doen vanaf de opdrachtregel?

Ik heb een hoofdmap met veel submappen, dus de oplossing zou recursief moeten werken.


62
2018-04-19 12:02


oorsprong


Zien askubuntu.com/questions/10607/... - belacqua


antwoorden:


Een draagbare manier (die werkt op elk POSIX-compatibel systeem):

find /the/path -depth -name "*.abc" -exec sh -c 'mv "$1" "${1%.abc}.edefg"' _ {} \;

In bash4 kun je globstar gebruiken om recursieve klodders te krijgen (**):

shopt -s globstar
for file in /the/path/**/*.abc; do
  mv "$file" "${file%.abc}.edefg"
done

De (perl) rename commando in Ubuntu kan bestanden hernoemen met behulp van perl reguliere expressiesyntaxis, die je kunt combineren met globstar of find:

# Using globstar
shopt -s globstar
files=(/the/path/**/*.abc)  

# Best to process the files in chunks to avoid exceeding the maximum argument 
# length. 100 at a time is probably good enough. 
# See http://mywiki.wooledge.org/BashFAQ/095
for ((i = 0; i < ${#files[@]}; i += 100)); do
  rename 's/\.abc$/.edefg/' "${files[@]:i:100}"
done

# Using find:
find /the/path -depth -name "*.abc" -exec rename 's/\.abc$/.edefg/' {} +

Zie ook http://mywiki.wooledge.org/BashFAQ/030 


76
2018-04-19 18:51



de eerste voegt eenvoudig de nieuwe extensie toe aan de oude, het vervangt niet ... - user2757729
@ user2757729, het wordt wel vervangen. "${1%.abc}" breidt uit naar de waarde van $1 behalve zonder .abc aan het einde. Zien faq 100 voor meer informatie over het manipuleren van shell-reeksen. - geirha
Ik stelde dit in op een bestandsstructuur van ~ 70k bestanden ... in ieder geval op mijn shell is het zeker niet vervangen. - user2757729
Sorry, ik heb zojuist een test dir-structuur opgezet en deze lijkt te werken. Vraag me af wat ik gisteren verkeerd heb gedaan. - user2757729
@ user2757729 Waarschijnlijk hebt u de "abc" achtergelaten ${1%.abc}... - kvnn


Dit zal de vereiste taak uitvoeren als alle bestanden zich in dezelfde map bevinden

rename 's/.abc$/.edefg/' *.abc

Om de bestanden een andere naam te geven, gebruik je dit recursief:

find /path/to/root/folder -type f -name '*.abc' -print0 | xargs -0 rename 's/.abc$/.edefg/'

37
2018-04-19 12:23



Of rename 's/.abc$/.edefg/' /path/to/root/folder/**/*.abc in een moderne versie van Bash. - Adam Byrtek
Hartelijk dank Adam voor het geven van een tip over het gebruik van * .abc in mappen recursief! - Rafał Cieślak
Goede tip, bedankt! Waar kan ik meer documentatie vinden over de kleine syntaxis van regex? Zoals wat is de s aan het begin, en welke andere opties kan ik daar gebruiken. Bedankt ! - Anto
@AdamByrtek Ik heb net gefaald met uw suggestie over Utopic. ('geen dergelijke bestanden' of iets dergelijks.) - Jonathan Y.


Een probleem met recursieve hernoemingen is dat welke methode u ook gebruikt om de bestanden te lokaliseren, het geeft het hele pad door rename, niet alleen de bestandsnaam. Dat maakt het moeilijk om ingewikkelde hernoemingen in geneste mappen te doen.

ik gebruik find's -execdir actie om dit probleem op te lossen. Als je gebruikt -execdir in plaats van -exec, de opgegeven opdracht wordt uitgevoerd vanuit de submap die het overeenkomende bestand bevat. Dus in plaats van het hele pad door te geven rename, het gaat alleen voorbij ./filename. Dat maakt het veel gemakkelijker om de regex te schrijven.

find /the/path -type f \
               -name '*.abc' \
               -execdir rename 's/\.\/(.+)\.abc$/version1_$1.abc/' '{}' \;

In detail:

  • -type f betekent alleen zoeken naar bestanden, niet naar mappen
  • -name '*.abc' betekent betekent alleen overeenkomen met bestandsnamen die eindigen op .abc
  • De backslashes erachter -type en -name zijn het bash-regel-vervolgkarakter. Ik gebruik ze om dit voorbeeld leesbaarder te maken, maar in de praktijk zijn ze niet nodig.
  • De backslash aan het einde van de -execdir regel is verplicht. Het is daar om de puntkomma te esacken, die de opdracht uitvoert die wordt uitgevoerd -execdir. Pret!

Uitleg van de regex:

  • s/ begin van de regex
  • \.\/ overeenkomen met de leidende ./ die -execdir binnenkomt. Gebruik \ om te ontsnappen aan. en / metacharacters
  • (.+) overeenkomen met de bestandsnaam. De haakjes leggen de overeenkomst vast voor later gebruik
  • \.abc ontsnappen aan de stip, overeenkomen met de abc
  • $ veranker de match aan het einde van de string

  • / markeert het einde van het "match" -gedeelte van de regex en het begin van het "replace" -gedeelte

  • version1_ voeg deze tekst toe aan elke bestandsnaam

  • $1 verwijst naar de bestaande bestandsnaam, omdat we deze met haakjes hebben vastgelegd. Als u meerdere sets haakjes gebruikt in het gedeelte 'overeenkomst', kunt u hier verwijzen naar $ 2, $ 3, etc.
  • .abc de nieuwe bestandsnaam eindigt op .abc. U hoeft hier niet te ontsnappen aan het puntmetateken in de sectie 'vervangen'
  • / einde van de regex

Voor

tree --charset=ascii

|-- a_file.abc
|-- Another.abc
|-- Not_this.def
`-- dir1
    `-- nested_file.abc

Na

tree --charset=ascii

|-- version1_a_file.abc
|-- version1_Another.abc
|-- Not_this.def
`-- dir1
    `-- version1_nested_file.abc

hint: rename's -n optie is handig. Het voert een droge run uit en laat zien welke namen het zal veranderen, maar brengt geen wijzigingen aan.


11
2017-10-05 22:48





Een andere draagbare manier:

find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "$1" "$(dirname "$1")/$(basename "$1" .abc).edefg"' _ '{}' \;

5
2018-02-03 16:36



Welkom bij Ask Ubuntu! Hoewel dit een waardevol antwoord is, raad ik aan het uit te breiden (door te bewerken) om uit te leggen hoe en waarom die opdracht werkt. - Eliah Kagan


# Rename all *.txt to *.text
for f in *.txt; do 
mv -- "$f" "${f%.txt}.text"
done

Zie ook de vermelding waarom je niet zou moeten analyseren ls.

Bewerken: als u basisnaam moet gebruiken, is uw syntaxis:

for f in *.txt; do
mv -- "$f" "$(basename "$f" .txt).text"
done

https://unix.stackexchange.com/questions/19654/changing-extension-to-multiple-files


0
2017-08-22 12:12





Dit is wat ik deed en werkte behoorlijk precies zoals ik wilde. Ik gebruikte de mv opdracht. Ik had meerdere .3gp bestanden en ik wilde ze allemaal hernoemen naar .mp4

Hier is een korte oneliner voor:

for i in *.3gp; do mv -- "$i" "ren-$i.mp4"; done

Die gewoon de huidige map doorzoekt, alle .3gp-bestanden oppikt en vervolgens de naam verandert (met behulp van de mv) ren-name_of_file.mp4


0
2018-03-13 21:58



Dat zal hernoemen file.3gp naar file.3gp.mp4, wat misschien niet is wat de meeste mensen willen. - muru
@muru maar het principe bestaat nog steeds. Selecteer gewoon ieder extensie en specificeer vervolgens de extensie van de bestemming om ze hernoemd te krijgen. De .3gp of .mp4 hier waren alleen ter illustratie. - Rexford
De syntaxis is te prefereren, één regel en gemakkelijk te begrijpen. Ik zou echter gebruiken basename "$i" .mp4 om de vorige extensie te verwijderen in plaats van "ren- $ i.mp4". - PTao
True. Goed punt. - Rexford


Ik vond een gemakkelijke manier om dit te bereiken. Om extensies van veel bestanden van jpg naar pdf te wijzigen, gebruik je:

for file in /path/to; do mv $file $(basename -s jpg $file)pdf ; done

0
2017-09-27 03:39





Ik zou de gebruiken mmv-opdracht van de pakket met dezelfde naam:

mmv ';*.abc' '#1#2.edefg'

De ; komt overeen met nul of meer */ en komt overeen met #1 in de vervanging. De * komt overeen met #2. De niet-recursieve versie zou zijn

mmv '*.abc' '#1.edefg'

0
2018-05-20 21:13