Mam taką strukturę katalogów:
./a/1.png
./a/2.png
./a/3.png
./b/1.png
./b/2.png
./b/3.png
./c/1.png
...
I chcę wziąć wszystkie pliki z podkatalogów i przenieść je do nowego katalogu, aby ich nazwy były podobne
../dest/a_1.png
../dest/a_2.png
../dest/a_3.png
../dest/b_1.png
../dest/b_2.png
../dest/b_3.png
../dest/c_1.png
...
Najbliższe, jakie udało mi się znaleźć bez pisania skryptu, aby to zrobić plik po pliku, to skorzystanie find
z --backup=numbered
opcji, która skropliłaby moje pliki do jednego katalogu, ale ostatecznie straciłaby kontekst katalogu z nazwy pliku.
Czy istnieje zwięzły sposób na osiągnięcie tego?
Odpowiedzi:
Za pomocą niezależnego
rename
polecenia Perla :Wynik:
Jeśli wszystko wygląda dobrze, usuń opcję
-n
.źródło
/
go_
wykonuje pracę w tym konkretnym przypadku, oczywiście.rename
- wygląda na użyteczne!mmv
jest także inne podejście Jochena Lutza z :rename -n 's|(.*)/(.*)|dest/$1_$2|' */*.png
Z Bash 4 i rekurencyjnym globowaniem (
shopt -s globstar
):Zauważ, że
dirname /foo/bar/baz
zwraca,/foo/bar
a nie tylkobar
, dlatego musisz wywołaćbasename
wynik, aby dostać siębar
na wypadek, gdybyś pracował z innego folderu nadrzędnego.źródło
Jako alternatywne podejście, nie trzeba zewnętrznych programów takich jak
rename
,basename
itp - to wszystko może być obsługiwane w ramachbash
dopełniania parametrów: -Rozszerzenie jest nieco trudne do śledzenia, więc oto co się stanie: -
find
.$f
.read -r
Parametr a cudzysłowie rozwinięć obsłużyć dziwne nazwy spacjami w nazwach plików.mv
Polecenie przenosi nazwiska jaka/b/c
doTargetDir/a_b_c
./
za_
, ale wygląda na trudne, ponieważ/
jest częścią składni substytucyjnego.${param/old/new}
, która zastępuje pierwszą instancjęold
przeznew
w rozszerzeniu.${param//old/new}
, które zastępuje każde wystąpienieold
przeznew
w ekspansji./
być częścią starego ciągu znaków, musi być poprzedzony znakiem ucieczki, ponieważ\/
stąd jest raczej niejasny${f//\//_}
: pierwszy/
wprowadza składnię podstawiania, drugi/
określa zastąpienie co , trzeci (znak zmiany znaczenia)/
jest starym ciągiem, a ostatni/
wprowadza nowy ciąg (_
).Często nie widzę tej formy rozszerzenia w skryptach, ale warto o tym wiedzieć, ponieważ czasami może być bardzo przydatna.
Istnieje kilka nazw plików, które to zepsują (osadzone znaki nowej linii oraz spacje początkowe lub końcowe, chociaż istnieją dwa sposoby na obejście tych ostatnich: -
read -r
zamiastread -r f
i użyjREPLY
zamiastf
.while f="$(line)"
zamiastread -r f
.Ten ostatni jest ładniejszy, ale korzysta z zewnętrznego programu
line
, który może nie być dostępny we wszystkich systemach, choć można go zakodować jako funkcję:źródło
bash
jako tag, stąd założenie.Alternatywą dla zmiany nazwy jest mmv , który wykorzystuje standardowe wzorce powłoki.
Ze strony podręcznika:
W przypadku użycia:
Podobnie jak zmiana nazwy , „-n” nie jest wykonywany. Usuń go, aby zmienić nazwę.
Oprócz zmiany nazwy, mmv obsługuje także kopiowanie, łączenie lub dołączanie plików.
źródło