Нет никакой определенной команды, которая может остановить файл, который будет несколько раз монтироваться с различными точками монтирования, но можно использовать этот сценарий для не монтирования его, если он уже смонтирован:
#!/bin/bash
mount |grep -qF "$1" || mount "$1" $2 -o loop
первый параметр является файлом для монтирования, и второй точка монтирования для использования.
mv[115619] не может объединять или перезаписывать каталоги, не получится с сообщением [115620]"mv: не может переместить 'a' в 'b': Каталог не пустой"[115621], даже при использовании опции [115622] --force[115623].
Вы можете работать с этим, используя другие инструменты (например, [115624]rsync[115625], [115626] find[115627], или даже [115628]cp[115629]), но необходимо тщательно продумать последствия:
rsync[115997] может объединить содержимое одной директории в другую (в идеале с опцией [115998]--remove-source-files
1[116001], чтобы безопасно удалять только те файлы исходных текстов, которые были успешно переданы, и с обычной опцией разрешения/владения/сохранения времени [116002]-a[116003], если хотите)[116004]. ... [116005], но [116006] это операция полного копирования, и поэтому она может быть очень ресурсоемкой для диска.
Предпочтительный в настоящее время вариант:[116008] Вы можете комбинировать [116009]rsync[116010] опцию [116011]--link-dest=DIR[116012] (чтобы создавать жесткие ссылки вместо копирования содержимого файлов, где это возможно) и [116013]--remove-source-files[116014], чтобы получить семантическое выражение, очень похожее на обычное [116015]mv[116016].[116017]. Для этого [116018]--link-dest[116019] должен быть указан абсолютный путь к каталогу [116020]-источника [116021] (или относительный путь из [116022]-направления [116023] к [116024]-источнику [116025]).[116026]. ... [116027], но [116028] это использование [116029]--link-dest[116030] непреднамеренным образом (который может вызвать или не вызвать осложнений), требует знания (или определения) абсолютного пути к источнику (в качестве аргумента к [116031]--link-dest[116032]), и снова оставляет пустую структуру каталога для очистки согласно [116033]1[116034].[1297] Вы можете использовать [116101] find[1298], чтобы последовательно воссоздать структуру исходного каталога в целевом месте, а затем индивидуально переместить фактические файлы[116037]. ... [116038], но[116039] это должно повторяться через источник несколько раз и может столкнуться с условиями гонки (новые каталоги создаются в источнике в процессе многоэтапного процесса)[1299]cp[116104] может создать жесткие связи[116041] (проще говоря, дополнительные указатели к тому же самому существующему файлу), что создает результат очень похожий на слияние [116042]mv[116043] (и очень эффективен с точки зрения ввода-вывода, так как создаются только указатели, а фактические данные не нужно копировать)[116044]. ... [116045], но[116046] это опять же страдает от возможного состояния гонки (новые файлы в исходном тексте удаляются, даже если они не были скопированы на предыдущем шаге)[12100] Какое из этих обходных путей (если таковые имеются) подходит, будет очень сильно зависеть от вашего конкретного случая использования.[115638]. Как всегда, подумайте, прежде чем выполнять любую из этих команд, и сделайте резервные копии.[12101]1: Обратите внимание, что [116047]rsync --remove-source-files[116048] не удалит ни одного каталога, поэтому вам нужно будет сделать что-то вроде [116049]find -depth -type d -empty -delete[116050] afterwards, чтобы избавиться от пустого дерева исходных каталогов.[12102]
Вот способ объединения каталогов. Это намного быстрее, чем rsync, поскольку он просто переименовывает файлы, а не копирует их, а затем удаляет.
cd source; find -type f -print0 | xargs -0 -n 1 -I {} mv '{}' 'dest/{}'
Я бы порекомендовал эти четыре шага:
cd ${SOURCE};
find . -type d -exec mkdir -p ${DEST}/\{} \;
find . -type f -exec mv \{} ${DEST}/\{} \;
find . -type d -empty -delete
или еще лучше, вот сценарий, который реализует семантику, подобную mv
:
#!/bin/bash
DEST="${@:${#@}}"
ABS_DEST="$(cd "$(dirname "$DEST")"; pwd)/$(basename "$DEST")"
for SRC in ${@:1:$((${#@} -1))}; do (
cd "$SRC";
find . -type d -exec mkdir -p "${ABS_DEST}"/\{} \;
find . -type f -exec mv \{} "${ABS_DEST}"/\{} \;
find . -type d -empty -delete
) done
Вот скрипт, который работал для меня. Я предпочитаю MV над rsync, поэтому я использую драгоценные камни и решения Джера и Джонатана Майера.
#!/bin/bash
# usage source1 .. sourceN dest
length=$(($#-1))
sources=${@:1:$length}
DEST=$(readlink -f ${!#})
for SRC in "$sources"; do
pushd "$SRC";
find . -type d -exec mkdir -p "${DEST}/{}" \;
find . -type f -exec mv {} "${DEST}/{}" \;
find . -type d -empty -delete
popd
done
Вы можете использовать опцию -L
-L команды CP , которая создает жестких ссылок файлов в одной и той же файловой системе вместо копий Full-Data. Следующая команда копирует папку Source / папку
в родительскую папку ( пункта назначения
), который уже содержит каталог с папкой
.
cp -rl source/folder destination
rm -r source/folder
Вы также можете использовать -P
( ( - no-dereference
- не деосвязи символические ссылки) или -
( - Архив
- сохранить все метаданные, также включает в себя -P
Опция), в зависимости от ваших потребностей.
Не рекомендуется использовать такие команды, как cp или rsync. Для больших файлов это займет много времени. mv намного быстрее, поскольку он обновляет только inodes без физического копирования файлов. Лучше использовать файловый менеджер вашей операционной системы. Для Opensuse есть файловый менеджер Konquerer. Он может перемещать файлы, не копируя их. Он имеет функцию «вырезать и вставить», как и в Windows. Просто выберите все подкаталоги в каталоге A. Щелкните правой кнопкой мыши и «перейдите в» каталог B, который может содержать подкаталоги с такими же именами. Он их объединит. Есть также варианты, хотите ли вы перезаписать или переименовать файлы с одинаковыми именами.
Быстрое решение Python, которое только один раз просматривает дерево исходных файлов
Так как я не мог найти удовлетворительного -существующего решения, я решил сделать быстрый скрипт Python для его реализации.
В частности, этот метод эффективен, потому что он просматривает дерево исходных файлов только один раз снизу вверх.
Это также позволит вам быстро настроить такие вещи, как перезапись файлов, по своему вкусу.
Использование:
move-merge-dirs src/ dest/
переместит все содержимое src/*
в dest/
, а src/
исчезнет.
переместить -объединить -каталоги
#!/usr/bin/env python3
import argparse
import os
def move_merge_dirs(source_root, dest_root):
for path, dirs, files in os.walk(source_root, topdown=False):
dest_dir = os.path.join(
dest_root,
os.path.relpath(path, source_root)
)
if not os.path.exists(dest_dir):
os.makedirs(dest_dir)
for filename in files:
os.rename(
os.path.join(path, filename),
os.path.join(dest_dir, filename)
)
for dirname in dirs:
os.rmdir(os.path.join(path, dirname))
os.rmdir(source_root)
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Move merge src/* into dest. Overwrite existing files.'
)
parser.add_argument('src_dir')
parser.add_argument('dest_dir')
args = parser.parse_args()
move_merge_dirs(args.src_dir, args.dest_dir)
Протестировано на Python 3.7, Ubuntu 18.04.
Это команда для перемещения файлов и папок в другое место назначения:
$ mv /source/path/folder /target/destination/
Помните, что команда:mv
не будет работать, если папка b& #x332;e& #x332;i& #x332;n& #x332;g m& #x332;e& #x332;r& #x332;ge& #x332;d& #x332;(т.е. другая папка с таким же именем уже существует в месте назначения )и d& #x332;e& #x332;s& #x332;t& #x332;i& #x332;n& #x332;a& #x332;t& #x332;i& #x332;o& #x332;n& #x332; o& #x332;n& #x332;e& #x332; i& #x332;s& #x332; n& #x332;o& #x332;t& #x332; e& #x332;m& #x332;pt& #x332;y .
mv: cannot move '/source/path/folder' to '/target/destination/folder': Directory not empty
Если папка назначения пуста, приведенная выше команда будет работать нормально.
Итак, чтобы в любом случае объединить обе папки,
Либо сделать это в 2 команды:
$ cp -rf /source/path/folder /target/destination/
$ rm -rf /source/path/folder
Или объединить обе команды в одну -команду времени:
$ cp -rf /source/path/folder /target/destination/ && rm -rf /source/path/folder
mv = move
cp = copy
rm = remove-r for directory (folder)
-f force execution
#!/bin/bash
# usage source1.. sourceN dest
length=$(($#-1))
sources=${@:1:$length}
DEST=$(readlink -f ${!#})
for SRC in $sources; do
pushd "$SRC";
# Only one scan - we only need folders with files
find. -type f | while read FILE ; do
DIRNAME=`dirname "$FILE"`
# Create the lowest level directory at once
if [ ! -d "$DEST/$DIRNAME" ] ; then
mkdir -v "$DEST/$DIRNAME"
fi
mv -v "$FILE" "$DEST/$FILE"
done
# Remove the directories no longer needed
find. -type -d | sort -r | xargs -i rmdir "{}"
popd
done
Вы можете объединить a и b с:
shopt -s dotglob
mv a/* b
До мв:
.
├── a
│ ├── c
│ │ └── x
│ └──.x
└── b
├── y
└──.y
После мв:
.
├── a
└── b
├── c
│ └── x
├──.x
├── y
└──.y
dotglob позволяет перемещать скрытые файлы точек, такие как .x
Используйте rmdir для удаления пустого каталога.
mv
с find
. Это можно сделать за один проход.cd "$SRC"
find -type d -exec mkdir -vp "$DST"/{} \; -or -exec mv -nv {} "$DST"/{} \;
… где $SRC
и $DST
— исходный и целевой каталоги соответственно.
-type d
проверяет, является ли элемент каталогом. Если это каталог, переходим к следующему действию или тесту :-exec …
. -exec … {} \;
{}
заменяется путем к текущему элементу относительно текущего рабочего каталога. \;
указывает на конец этой команды -exec …
. mkdir -pv …
, -pv
эквивалентно -p -v
. -p
означает создание всех промежуточных каталогов по мере необходимости и отсутствие ошибки, если каталог уже существует. -v
означает --verbose
и просто говорит ему печатать сообщение для каждого созданного каталога, чтобы вы могли видеть, что он делает. "$DST"/{}
будет расширен до каталога назначения, включая все необходимые кавычки. -or
— интересная часть, которая позволяет нам сделать это за один проход. С помощью команды find
каждый тест (, например, -type d
), или действие (, например, -exec …
), приводят к состоянию истинного или ложного, в зависимости от того, пройден ли тест или действие выполнено успешно. Тесты и действия могут быть связаны с помощью -and
, -or
, -not
, -true
, -false
и \( … \)
. Когда вы добавляете несколько тестов и/или действий без явного логического оператора, они неявно объединяются по И. Таким образом, приведенная выше команда эквивалентна этой:find \( -type d -and -exec mkdir -vp "$DST"/{} \; \) -or -exec mv -nv {} "$DST"/{} \;
. Таким образом, если -type d
проходит,затем он переходит к следующему действию (-exec …
). Если нет, то первая ветвь -or
является ложной и переходит ко второй ветви, которая охватывает все, что не является каталогом (, например, файлы ). mv -nv {} "$DST"/{}
-nv
эквивалентно -n -v
. -n
говорит ему не перезаписывать какие-либо файлы в каталоге назначения. -v
говорит ему сообщать о сообщении для каждого перемещенного файла, чтобы вы могли видеть, что он делает. find
по умолчанию использует ширину -первый обход. {}
НЕ нужно заключать в кавычки, даже если элемент, который он обозначает, содержит пробелы. Если вы хотите скопировать /usr/local в /usr, вы можете ввести его следующим образом.
cd /usr/local
find -type d -exec mkdir -vp../{} \; -or -exec mv -nv {}../{} \;
Это приведет к таким командам:
mkdir -pv.././bin
mv -nv./bin/pip.././bin/pip
mv -nv./bin/pip3.././bin/pip3
mv -nv./bin/python3.././bin/python3
mv -nv./bin/python3.././bin/python3
mv -nv./bin/xxhsum.././bin/xxhsum
mkdir -pv.././etc
mkdir -pv.././include
mv -nv./include/xxh3.h.././include/xxh3.h
mv -nv./include/xxhash.h.././include/xxhash.h
… и так далее
Чтобы увидеть, какие команды будут выполняться, добавьте echo
перед каждой командой, сразу после -exec
, вот так:
cd "$SRC"
find -type d -exec echo mkdir -vp "$DST"/{} \; -or -exec echo mv -nv {} "$DST"/{} \;
‾‾‾‾ ‾‾‾‾
Просто хотел поделиться своим решением похожей проблемы. У меня беспорядок. Несколько копий каталога, где каждая копия имеет изменения в файлах, и в основном просто нужно объединить их вместе:
Эта последняя операция будет еще одним шагом, на котором я создаю список дубликатов файлов в этих каталогах на основе сравнений inode/size/md5sum, а затем решаю, следует ли -жестко связать или просто удалить дубликаты (как контролировать какой из них сохранить, я еще не решил ).
Тем не менее, я планирую завершить свои первые операции следующими:
# hard-link over any files that don't exist in the destination while removing them from source
rsync -livrHAX --remove-source-files --ignore-existing --link-dest=../src/ src/ dst/
# move over existing files that are newer and remove them from source, keeping backup of ones that were replaced
# (after verifying during test drills that inodes of moved files are the same, I conclude that this doesn't slow-copy files, but YMMV or check rsync source for your OS/arch/filesystem)
rsync -buvaiHAX --remove-source-files --suffix=".bak_older" src/ dst/
# move over the rest of the files that are older than the ones in the destination, remove them from source, and retain backups of ones that were replaced
rsync -bvaiHAX --remove-source-files --suffix=".bak_newer" src/ dst/
# remove empty directories recursively
find src -type d -exec rmdir -p "{}" \; 2>/dev/null
# src/ should hopefully now no longer exist
# check for averted clobbers against older files to manually verify that the replacements are acceptable
find dst -name '*.bak_older'
# check for averted clobbers again newer files to manually verify that the replacements aren't out-dated (in terms of whatever is important to you)
find dst -name '*.bak_older'
Я сообщу об этом в этом посте, если у меня будут какие-либо серьезные обновления в моих процедурах, но на самом деле это похоже на быструю и безопасную операцию «слияния каталогов»
Приведенные выше ответы хороши, но выполнение этого процесса заставило меня нервничать.Я хотел поделиться тестовым скриптом, демонстрирующим, что на самом деле делает метод rsync.
reset_rsync_test_local_move(){
LOCAL_DPATH=$HOME/tmp/rsync-test/local
MOVE_TEST_ROOT=$LOCAL_DPATH/rsync_move_test
# Setup home data
echo "LOCAL_DPATH = $LOCAL_DPATH"
if [ -d "$LOCAL_DPATH" ]; then
rm -rf $LOCAL_DPATH
fi
mkdir -p $LOCAL_DPATH
mkdir -p $MOVE_TEST_ROOT
# Pretend that we accidently botched a move and have a repo inside of a repo
# so the goal is merge all files from repo/repo into repo
mkdir -p $MOVE_TEST_ROOT/repo/
mkdir -p $MOVE_TEST_ROOT/repo/primes/
mkdir -p $MOVE_TEST_ROOT/repo/perfect/
mkdir -p $MOVE_TEST_ROOT/repo/nat/
mkdir -p $MOVE_TEST_ROOT/repo/repo
mkdir -p $MOVE_TEST_ROOT/repo/repo/primes/
mkdir -p $MOVE_TEST_ROOT/repo/repo/perfect/
mkdir -p $MOVE_TEST_ROOT/repo/repo/nat/
# Some of the primes ended up in the correct and the botched repo
touch $MOVE_TEST_ROOT/repo/primes/prime02
touch $MOVE_TEST_ROOT/repo/primes/prime05
touch $MOVE_TEST_ROOT/repo/primes/prime13
touch $MOVE_TEST_ROOT/repo/repo/primes/prime03
touch $MOVE_TEST_ROOT/repo/repo/primes/prime11
# For prime7, lets say there is a conflict in the data contained in the file
echo "correct data" > $MOVE_TEST_ROOT/repo/primes/prime07
echo "botched data" > $MOVE_TEST_ROOT/repo/repo/primes/prime07
# All of the perfects ended up in the botched repo
touch $MOVE_TEST_ROOT/repo/repo/perfect/perfect006
touch $MOVE_TEST_ROOT/repo/repo/perfect/perfect028
touch $MOVE_TEST_ROOT/repo/repo/perfect/perfect496
# The naturals have some symlinks, so we need to be careful there
touch $MOVE_TEST_ROOT/repo/nat/nat04
touch $MOVE_TEST_ROOT/repo/nat/nat06
# basedir nats
touch $MOVE_TEST_ROOT/repo/nat/nat01
ln -s $MOVE_TEST_ROOT/repo/primes/prime02 $MOVE_TEST_ROOT/repo/nat/nat02
(cd $MOVE_TEST_ROOT/repo/nat/ && ln -s../primes/prime05 nat05)
ln -s $MOVE_TEST_ROOT/repo/primes/prime11 $MOVE_TEST_ROOT/repo/nat/nat11
# Botched nats
touch $MOVE_TEST_ROOT/repo/repo/nat/nat08
ln -s $MOVE_TEST_ROOT/repo/primes/prime07 $MOVE_TEST_ROOT/repo/repo/nat/nat07
(cd $MOVE_TEST_ROOT/repo/repo/nat/ && ln -s ../primes/prime03 nat03)
ln -s $MOVE_TEST_ROOT/repo/repo/primes/prime11 $MOVE_TEST_ROOT/repo/repo/nat/nat11
tree $MOVE_TEST_ROOT
}
test_rsync_merge_folders(){
__doc__="
source ~/misc/tests/bash/test_rsync.sh
"
reset_rsync_test_local_move
# Does not work
#mv $MOVE_TEST_ROOT/repo/repo/* $MOVE_TEST_ROOT/repo
rsync -avrRP $MOVE_TEST_ROOT/repo/./repo $MOVE_TEST_ROOT
tree $MOVE_TEST_ROOT
# Check the content of prime7 to see if it was overwritten or not
# Ans: the data is not overwritten, only disjoint files are merged in
cat $MOVE_TEST_ROOT/repo/primes/prime07
# Remove the botched repo
rm -rf $MOVE_TEST_ROOT/repo/repo
# Note that the broken (nat11) link is overwritten
tree $MOVE_TEST_ROOT
}
Приведенный выше скрипт создаст тестовый каталог с «правильным» и «неправильным» репозиторием. По сути, у нас должно быть репозиторий с папками для натуральных, простых и совершенных чисел, но что-то пошло не так, и некоторые данные существуют в правильном месте, но мы случайно создали репозиторий/репозиторий подпапок, который содержит часть данных. Цель состоит в том, чтобы объединить все из./repo/repo в./repo
Начальная структура каталогов выглядит следующим образом:
/home/joncrall/tmp/rsync-test/local/rsync_move_test
└── repo
├── nat
│ ├── nat01
│ ├── nat02 -> /home/joncrall/tmp/rsync-test/local/rsync_move_test/repo/primes/prime02
│ ├── nat04
│ ├── nat05 ->../primes/prime05
│ ├── nat06
│ └── nat11 -> /home/joncrall/tmp/rsync-test/local/rsync_move_test/repo/primes/prime11
├── perfect
├── primes
│ ├── prime02
│ ├── prime05
│ ├── prime07
│ └── prime13
└── repo
├── nat
│ ├── nat03 ->../primes/prime03
│ ├── nat07 -> /home/joncrall/tmp/rsync-test/local/rsync_move_test/repo/primes/prime07
│ ├── nat08
│ └── nat11 -> /home/joncrall/tmp/rsync-test/local/rsync_move_test/repo/repo/primes/prime11
├── perfect
│ ├── perfect006
│ ├── perfect028
│ └── perfect496
└── primes
├── prime03
├── prime07
└── prime11
Примечание. Я добавил несколько относительных и абсолютных символических ссылок, чтобы проверить, как это работает с ними.
После выполнения:
rsync -avrRP $MOVE_TEST_ROOT/repo/./repo $MOVE_TEST_ROOT
Получаем:
/home/joncrall/tmp/rsync-test/local/rsync_move_test
└── repo
├── nat
│ ├── nat01
│ ├── nat02 -> /home/joncrall/tmp/rsync-test/local/rsync_move_test/repo/primes/prime02
│ ├── nat03 ->../primes/prime03
│ ├── nat04
│ ├── nat05 ->../primes/prime05
│ ├── nat06
│ ├── nat07 -> /home/joncrall/tmp/rsync-test/local/rsync_move_test/repo/primes/prime07
│ ├── nat08
│ └── nat11 -> /home/joncrall/tmp/rsync-test/local/rsync_move_test/repo/repo/primes/prime11
├── perfect
│ ├── perfect006
│ ├── perfect028
│ └── perfect496
├── primes
│ ├── prime02
│ ├── prime03
│ ├── prime05
│ ├── prime07
│ ├── prime11
│ └── prime13
└── repo
├── nat
│ ├── nat03 ->../primes/prime03
│ ├── nat07 -> /home/joncrall/tmp/rsync-test/local/rsync_move_test/repo/primes/prime07
│ ├── nat08
│ └── nat11 -> /home/joncrall/tmp/rsync-test/local/rsync_move_test/repo/repo/primes/prime11
├── perfect
│ ├── perfect006
│ ├── perfect028
│ └── perfect496
└── primes
├── prime03
├── prime07
└── prime11
где все почти правильно перемещено. Единственная проблема заключается в том, что nat11 в «правильном» репозитории был неработающей символической ссылкой, поэтому он был перезаписан данными из «неудачного» подрепозитория. Другие файлы не перезаписываются, объединяются только непересекающиеся данные.
Удаление неудачного подкаталога дает нам:
/home/joncrall/tmp/rsync-test/local/rsync_move_test
└── repo
├── nat
│ ├── nat01
│ ├── nat02 -> /home/joncrall/tmp/rsync-test/local/rsync_move_test/repo/primes/prime02
│ ├── nat03 ->../primes/prime03
│ ├── nat04
│ ├── nat05 ->../primes/prime05
│ ├── nat06
│ ├── nat07 -> /home/joncrall/tmp/rsync-test/local/rsync_move_test/repo/primes/prime07
│ ├── nat08
│ └── nat11 -> /home/joncrall/tmp/rsync-test/local/rsync_move_test/repo/repo/primes/prime11
├── perfect
│ ├── perfect006
│ ├── perfect028
│ └── perfect496
└── primes
├── prime02
├── prime03
├── prime05
├── prime07
├── prime11
└── prime13
Таким образом, метод rsync в основном работает, просто будьте осторожны, потому что любые символические ссылки, которые не разрешены, могут быть перезаписаны.