S tímto příkazem jsem uspěl.
rename JPG jpg *.JPG
Kde rename
je příkaz, který říká shellu, aby přejmenoval každý výskyt JPG
do jpg
v aktuální složce se všemi názvy souborů s příponou JPG
.
Pokud vidíte, že Bareword "JPG" není povoleno, zatímco "strict subs" je používáno na (hodnota 1) řádku 1 s tímto přístupem, zkuste:
rename 's/\.JPG$/.jpg/' *.JPG
Řešení
Úlohu můžete vyřešit v jednom řádku:
find . -name '*.*' -exec sh -c '
a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$0" ] && mv "$0" "$a" ' {} \;
Poznámka:toto se přeruší u názvů souborů, které obsahují nové řádky. Ale zatím se mnou mějte strpení.
Příklad použití
$ mkdir C; touch 1.TXT a.TXT B.TXT C/D.TXT
$ find .
.
./C
./C/D.TXT
./1.TXT
./a.TXT
./B.TXT
$ find . -name '*.*' -exec sh -c 'a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/"); [ "$a" != "$0" ] && mv "$0" "$a" ' {} \;
$ find .
.
./C
./C/D.txt
./a.txt
./B.txt
./1.txt
Vysvětlení
Všechny soubory najdete v aktuálním adresáři (.
), které mají tečku .
ve svém názvu (-name '*.*'
) a spusťte příkaz pro každý soubor:
a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$0" ] && mv "{}" "$a"
Tento příkaz znamená:zkuste převést příponu souboru na malá písmena (to znamená sed
):
$ echo 1.txt | sed -r "s/([^.]*)\$/\L\1/"
1.txt
$ echo 2.TXT | sed -r "s/([^.]*)\$/\L\1/"
2.txt
a výsledek uložte do a
proměnná.
Pokud se něco změnilo [ "$a" != "$0" ]
, přejmenujte soubor mv "$0" "$a"
.
Název zpracovávaného souboru ({}
) předán sh -c
jako jeho další argument a na příkazovém řádku je vidět jako $0
.To dělá skript bezpečným, protože v tomto případě shell bere {} jako data, nikoli jako část kódu, jako když je specifikován přímo v příkazovém řádku.(Děkuji @gniourf_gniourf za upozornění na tento opravdu důležitý problém ).
Jak můžete vidět, pokud použijete {}
přímo ve skriptu je možné mít v názvech souborů nějaké shell-injekce, něco jako:
; rm -rf * ;
V tomto případě bude vkládání považováno shellem za součást kódu a budou provedeny.
Zatímco-verze
Jasnější, ale o něco delší verze skriptu:
find . -name '*.*' | while IFS= read -r f
do
a=$(echo "$f" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$f" ] && mv "$f" "$a"
done
Toto se stále přeruší u názvů souborů obsahujících nové řádky. Chcete-li tento problém vyřešit, musíte mít find
který podporuje -print0
(jako GNU find
) a Bash (takže read
podporuje -d
oddělovací spínač):
find . -name '*.*' -print0 | while IFS= read -r -d '' f
do
a=$(echo "$f" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$f" ] && mv "$f" "$a"
done
Toto stále nefunguje u souborů, které obsahují koncové nové řádky (protože je pohltí a=$(...)
subshell. Pokud opravdu chcete spolehlivou metodu (a měli byste!) s nejnovější verzí Bash (Bash≥4.0), která podporuje ,,
rozšíření parametrů zde je konečné řešení:
find . -name '*.*' -print0 | while IFS= read -r -d '' f
do
base=${f%.*}
ext=${f##*.}
a=$base.${ext,,}
[ "$a" != "$f" ] && mv -- "$f" "$a"
done
Zpět k původnímu řešení
Nebo v jednom find
přejít (zpět k původnímu řešení s několika opravami, díky kterým je opravdu spolehlivé):
find . -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \;
Přidal jsem -type f
takže se přejmenovávají pouze běžné soubory. Bez toho můžete mít stále problémy, pokud jsou názvy adresářů přejmenovány před názvy souborů. Pokud chcete také přejmenovat adresáře (a odkazy, roury atd.), měli byste použít -depth
:
find . -depth -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \;
takže find
provede prohledávání do hloubky.
Můžete namítnout, že není efektivní vytvořit bash
proces pro každý nalezený soubor. To je správně a předchozí verze smyčky by pak byla lepší.
Toto je kratší, ale obecnější v kombinaci s odpovědí ostatních:
rename 's/\.([^.]+)$/.\L$1/' *
Simulace
Pro simulaci použijte -n
, tj. rename -n 's/\.([^.]+)$/.\L$1/' *
. Tímto způsobem můžete vidět, co se změní, než budou provedeny skutečné změny. Příklad výstupu:
Happy.Family.GATHERING.JPG renamed as Happy.Family.GATHERING.jpg
Hero_from_The_Land_Across_the_River.JPG renamed as Hero_from_The_Land_Across_the_River.jpg
rAnD0m.jPg1 renamed as rAnD0m.jpg1
Krátké vysvětlení syntaxe
- Syntaxe je
rename OPTIONS 's/WHAT_TO_FIND_IN_THE_NAME/THE_REPLACEMENT/' FILENAMES
\.([^.]+)$
znamená posloupnost čehokoli kromě tečky ([^.]
) na konci řetězce ($
), za tečkou (\.
).\L$1
znamená tečku (\.
) následované malými písmeny (\L
) z 1 skupiny ($1
)- První skupinou je v tomto případě rozšíření (
[^.]+
) - Raději použijte jednoduché uvozovky
'
místo dvojitých uvozovek"
zabalit regulární výraz, aby se zabránilo expanzi shellu