BASH - scal katalogi podczas korzystania z mv

5

Właśnie zacząłem używać BASH, więc mogę przenieść całą zawartość katalogu do innego. Ale problem polega na tym, że spodziewałem się, że połączy katalogi. Używam zadania cron do uruchomienia tego skryptu.

#!/bin/bash

shopt -s dotglob nullglob
mv mv_schedule/* public_html/

Źródło: /mv_schedule/(zawierające foldery / pliki)

/files/
4.html
5.html
/assets/
sitemap.xml

Miejsce docelowe: /public_html/(poprzednie foldery już istnieją)

/files/
1.html
2.html
3.html
/assets/
sitemap.xml

Chcę więc, aby wszystkie nowe pliki z tych katalogów w środku /mv_schedule/połączyły się z plikami wewnątrz /public_html/, a pliki, które już tam są, zostały zastąpione plikami pochodzącymi /mv_schedule/.

Obecnie zwraca ten błąd przez e-mail, gdy próbuję tego z bieżącym skryptem:

mv: cannot move `mv_schedule/assets' to `public_html/assets': Directory not empty

Jak mogę to naprawić?

Bash
źródło
Czy używasz tego skryptu bash jako użytkownik root lub zwykły użytkownik? jeśli jest to użytkownik root, to jaka jest wydajność alias -a/
max
Niestety nie jestem pewien. Jestem tylko na cPanelu hostingu współdzielonego, więc prawdopodobnie nie mam prawdziwego dostępu do roota.
The Bash,

Odpowiedzi:

6

Komunikat o błędzie mówi ci, że nie można przenieść katalogu zasobów, ponieważ są w nim pliki. mv katalog oznacza wykonanie kopii, a następnie skasowanie i nie można go usunąć, ponieważ nadal zawiera niektóre pliki. Oznacza to, że powinieneś również zapisać pliki wewnątrz zasobu, a nawet podkatalogi i pliki w podkatalogach w zasobie, jeśli istnieją. Ale wydane polecenie mv może tylko kopiować (i wymazywać) to, co jest obecne w katalogu mv_schedule, a nie to, co jest obecne w podkatalogach (jak zasób) w mv_schedule.

To, czego chcesz, to polecenie, które obniża drzewo katalogów do ostatniego liścia i kopiuje je, coś w rodzaju opcji -r lub -R w rm, chmod, chown. Jednak mv nie ma takiej opcji, więc będziesz musiał użyć polecenia find, które zstępuje z całego drzewa katalogów od określonego katalogu głównego, a następnie wykonuje określone działanie. W twoim przypadku odpowiednie polecenie to:

SOURCE_DIR=$1
TARGET_DIR=$2
find $SOURCE_DIR -name '*' -type f -exec mv -f {} $TARGET_DIR \;

Spowoduje to spiętrzenie wszystkich plików w jednym katalogu docelowym. Zakłada się, że przekazałeś katalog źródłowy i docelowy jako parametry wiersza wejściowego do skryptu bash. Opcja -f zapobiega pytaniu o potwierdzenie w przypadku zastąpienia, możesz zmienić to na -n (nie zastępuj) lub -i (pytaj przed zastąpieniem).

Jeśli zamiast tego chcesz zachować strukturę katalogów, pamiętaj, że polecenie cp ma możliwość zejścia z drzewa katalogów, dzięki czemu możesz użyć tego, a następnie komendy rm, ponieważ to polecenie ma również możliwość zejścia z drzew. Jednym z możliwych zestawów poleceń jest:

SOURCE_DIR=$1
TARGET_DIR=$2
cp -a $SOURCE_DIR $TARGET_DIR
rm -rf $SOURCE_DIR

Zwróć uwagę na opcję -a w cp: zachowuje znaczniki czasu i własność. Jeśli nie przejmujesz się takimi rzeczami, możesz zamiast tego użyć opcji -R.

MariusMatutiae
źródło
Obawiam się, że nie rozumiem całkowicie twojej odpowiedzi. Ale chciałbym umieścić SOURCE_DIRi TARGET_DIRz dirName/? Widzę też, że wspomniałeś -i (zapytaj przed nadpisaniem). Chcę, aby zastąpił istniejące pliki i scalił z istniejącymi katalogami, aby wszystkie istniejące pliki były w katalogu docelowym i dodawać (również zastępować istniejące pliki) nowe z tego samego katalogu nazw ze źródła.
The Bash,
możesz ustawić SOURCE_DIR i TARGET_DIR na żądane wartości, w tym przypadku SOURCE_DIR = mv_schedule, a następnie TARGET_DIR = public_html. Możesz zastąpić za pomocą opcji -f, ale nie wyjaśniłeś, co chcesz zrobić z plikami, powiedzmy, zasobem katalogu. Czy powinny iść 1) do katalogu public_html, czy powinny do katalogu public_html / asset? Dwie zaproponowane przeze mnie opcje wykonują odpowiednio te dwie różne rzeczy.
MariusMatutiae
Dodałem kilka linii do mojej odpowiedzi, próbując to wyjaśnić.
MariusMatutiae
rm -rf $SOURCE_DIRusunie katalog źródłowy, być może lepiej go zachować i używać rm -rf "$SOURCE_DIR/*"- Zaleca się również stosowanie cudzysłowów wokół zmiennych (w przypadku gdy w nazwach katalogów występują spacje)
Savvas Radevic
Cóż, ponieważ wiem, że nie można użyć mv do przeniesienia plików. Chciałbym pozostawić zawartość katalogu źródłowego nienaruszoną. I aby wyjaśnić jeszcze raz, aby wszyscy rozumieli, jest to „tylko” zawartość samego /mv_schedule/, not the directory / mv_schedule / `.
The Bash,
2

Bardziej ogólna dyskusja na temat tego problemu znajduje się w sekcji Uniksa.

Możesz użyć -lopcji polecenia cp , która tworzy twarde łącza plików w tym samym systemie plików zamiast kopii pełnych danych. Następujące polecenie kopiuje folder source/folderdo folderu nadrzędnego ( destination), który już zawiera katalog o nazwie folder.

cp -rl source/folder destination
rm -r source/folder

Uwagi:

  • Możesz także użyć -P( --no-dereference- nie usuwaj odsyłaczy symbolicznych) lub -a( --archive- zachowaj wszystkie metadane, w tym także -Popcję), w zależności od potrzeb.
  • Mimo że zaangażowane są dwa kroki „I / O”, kroki te są stosunkowo prostymi operacjami na metadanych obejmującymi zerowe transfery „danych”. Zatem ta metoda jest o magnitudo szybsza niż -lrozwiązanie oparte na cp (sans ) lub rsync.
  • To nie działa, jeśli folder źródłowy i docelowy znajdują się w różnych systemach plików
palswim
źródło
1

Doskonała odpowiedź od palswim. (Również dzięki za głosowanie za te dwa ostatnie punkty potrzebne, aby móc głosować sam!)

CP-R1; rm rozwiązuje problem i przy minimalnym We / Wy (cp i rsync są pod tym względem głupio drogie).

Są z tym dwa drobne problemy:

  • nie jest w 100% atomowy - wymaga dwóch operacji I / O (dotyczy to również metody rsync, ponieważ --remove-source-files nadal wymaga osobnej operacji I / O).
  • nie działa, jeśli źródło i miejsce docelowe znajdują się w różnych systemach plików
zaTricky
źródło