Как вы перемещаете все файлы (в том числе скрытые) из одного каталога в другой?


135

Как переместить все файлы в каталоге (включая скрытые) в другой каталог?

Например, если у меня есть папка «Foo» с файлами «.hidden» и «notHidden» внутри, как мне переместить оба файла в каталог с именем «Bar»? Следующее не работает, так как «.hidden» файл остается в «Foo».

mv Foo/* Bar/

Попробуй сам.

mkdir Foo
mkdir Bar
touch Foo/.hidden
touch Foo/notHidden
mv Foo/* Bar/


Бах, я знал, что должен был обновить ответы, прежде чем набирать свои. Очень полезная ссылка.
Стивен Д.

К сожалению, этот вопрос закончился здесь, потому что я сказал на SO, что он должен переместиться сюда или SU, поэтому, конечно, он переехал сюда, когда уже был дубликат на SU :)
Michael Mrozek

Лично я думаю, что * nix конкретные вопросы и ответы должны быть не по теме на SU. С текущими правилами мы в конечном итоге сталкиваемся с множеством конфликтов контента - как этот вопрос.
Кори Кляйн

Ответы:


154

Zsh

mv Foo/*(DN) Bar/

или же

setopt -s glob_dots
mv Foo/*(N) Bar/

(Не указывайте, (N)если каталог не пуст.)

удар

shopt -s dotglob nullglob
mv Foo/* Bar/

ksh93

Если вы знаете, что каталог не пуст:

FIGNORE='.?(.)'
mv Foo/* Bar/

Стандарт (POSIX) ш

for x in Foo/* Foo/.[!.]* Foo/..?*; do
  if [ -e "$x" ]; then mv -- "$x" Bar/; fi
done

Если вы хотите, чтобы mvкоманда возвращала статус ошибки, даже если она прошла успешно, это намного проще:

mv Foo/* Foo/.[!.]* Foo/..?* Bar/

GNU найти и GNU MV

find Foo/ -mindepth 1 -maxdepth 1 -exec mv -t Bar/ -- {} +

Стандартная находка

Если вы не возражаете перейти на исходный каталог:

cd Foo/ &&
find . -name . -o -exec sh -c 'mv -- "$@" "$0"' ../Bar/ {} + -type d -prune

Вот более подробно о контроле соответствия файлов точек в bash, ksh93 и zsh.

удар

Установите dotglobопцию .

$ echo *
none zero
$ shopt -s dotglob
$ echo *
..two .one none zero

Существует также более гибкая GLOBIGNOREпеременная , которую вы можете установить в список символов подстановки, разделенных двоеточиями, чтобы игнорировать их. Если не установлено (настройка по умолчанию), оболочка ведет себя так, как если бы значение было пустым, если dotglobустановлено, и как если бы значение было, .*если опция не установлена. См. Расширение имени файла в руководстве. Повсеместная каталоги .и ..всегда опущена, если .не будет сопоставляться явно с шаблоном.

$ GLOBIGNORE='n*'
$ echo *
..two .one zero
$ echo .*
..two .one
$ unset GLOBIGNORE
$ echo .*
. .. ..two .one
$ GLOBIGNORE=.:..
$ echo .*
..two .one

ksh93

Установите FIGNOREпеременную . Если не установлено (настройка по умолчанию), оболочка ведет себя так, как если бы значение было .*. Чтобы игнорировать .и .., они должны совпадать явно (руководство в ksh 93s + 2008-01-31 утверждает, что .и ..всегда игнорируется, но это не правильно описывает реальное поведение).

$ echo *
none zero
$ FIGNORE='@(.|..)'
$ echo *
..two .one none zero
$ FIGNORE='n*'
$ echo *
. .. ..two .one zero

Вы можете включать точечные файлы в шаблон , сопоставляя их явно.

$ unset FIGNORE
$ echo @(*|.[^.]*|..?*)
..two .one none zero

Чтобы расширение оказалось пустым, если каталог пуст, используйте параметр Nсопоставления с образцом: ~(N)@(*|.[^.]*|..?*)или ~(N:*|.[^.]*|..?*).

Zsh

Установите dot_globопцию .

% echo *
none zero
% setopt dot_glob
% echo *
..two .one none zero

.и ..никогда не совпадают, даже если шаблон .явно соответствует ведущему .

% echo .*
..two .one

Вы можете включать точечные файлы в определенный шаблон с помощью D квалификатора glob .

% echo *(D)
..two .one none zero

Добавьте NГлоб классификатор , чтобы сделать расширение выйти пустой в пустом каталоге: *(DN).


Примечание: вы можете получить имя файла результатов расширения в различных порядках (например, с noneпоследующим .oneпоследующим ..two) , основываясь на ваших настройках LC_COLLATE, LC_ALLи LANGпеременных.


1
Почему nullglob? Было бы более разумно прервать выполнение mvкоманды (например, fish, csh / tcsh, zsh (без (N)) do или bash с failglob), чем запускать mv /Bar/команду, которая не имеет большого смысла.
Стефан Шазелас

1
Я бы сказал, что ваше описание GLOBIGNORE неточно и вводит в заблуждение. Установка непустого значения для GLOBIGNORE включает dotglob и делает это .и ..никогда не совпадает (как в других разумных оболочках, таких как zsh, fish, pdksh и производные), даже если впоследствии вы выключите dotglob. ( GLOBIGNORE=:; shopt -u dotglob; echo .*не будет выводить .и ..). установка GLOBIGNORE в .:..или .или :иметь тот же эффект.
Стефан Шазелас

ksh93всегда игнорирует .и, ..кажется, был сломан между ksh93k+(где это работало) и ksh93m(где это больше не работает). Обратите внимание, что это плохо, так как ksh93будет принимать значение FIGNORE из среды, поэтому, например, FIGNORE='!(..)'env var может создать хаос.
Стефан Шазелас

24
#!/bin/bash

shopt -s dotglob
mv Foo/* Bar/

Из man bash

dotglob Если установлено, bash включает имена файлов, начинающиеся с '.' в результатах расширения пути.


С оговоркой, что эта команда возвращает код ошибки, если каталог был пуст (даже если команда фактически выполнялась как задумано).
Жиль

1
@ Жиль, тогда ошибка будет в mvтом, что ты ждешь, что этот файл Foo/*не существует. Интересно, что если Fooон доступен для поиска, но не доступен для чтения, и там есть файл, вызываемый *там, вы не получите никакой ошибки, этот файл будет перемещен, но не другие файлы в каталоге. bashимеет failglobопцию , чтобы он ведет себя больше как zsh, fishили csh/ tcshи прервать команду с ошибкой , когда Glob не может быть расширен вместо этого поддельного поведения оставляя Glob как есть.
Стефан Шазелас

8

Простой способ сделать это в bashэто

mv {Foo/*,Foo/.*} Bar/

Но это также переместит каталоги.

Если вы хотите переместить все файлы, включая скрытые, но не хотите перемещать каталог, вы можете использовать цикл for и test.

for i in $(ls -d {Foo/*,Foo/.*});do test -f $i && mv -v $i Bar/; done;


4

Одним из способов является использование find:

find Foo/ -type f -exec mv -t Bar/ {} \+

-type fОграничивает команды поиска для поиска файлов. Вам следует изучить параметры -type, -maxdepthи -mindepth, findчтобы настроить вашу команду для учета подкаталогов. Find имеет длинную, но очень полезную справочную страницу .


3
Это только перемещает обычные файлы Foo/, а не подкаталоги и другие файлы.
Жиль

2

Попробуйте команду копирования cp:

$ cp -r myfolder/* destinationfolder

cp -r означает копирование рекурсивно, поэтому все папки и файлы будут скопированы.

Вы можете использовать команду удаления, rmчтобы удалить папку:

$ rm -r myfolder

Подробнее: переместить все файлы из одного каталога в другой .


2
Не самое лучшее, когда вы перемещаете много больших файлов, но я думаю, это сработает.
Кори Кляйн

Может быть, если вы используете точку вместо звезды?
Винсент

1

Rsync - это еще один вариант:

rsync -axvP --remove-source-files sourcedirectory/ targetdirectory

Это работает, потому что в rsync косая черта имеет значение, sourcedirectory/относится к содержимому каталога, а sourcedirectoryотносится к самому каталогу.

Недостатком этого метода является то, что rsync будет очищать только файлы после перемещения, а не каталог. Таким образом, у вас остается пустое дерево источников. Обходные пути для этого см .:

Перемещать файлы и удалять каталоги с помощью rsync?

Поэтому, хотя это может быть неоптимальным для операций перемещения, оно может быть чрезвычайно полезным для операций копирования.


1

Ответ для Баш / Рыба

Вот способ сделать это с помощью подстановочных знаков:

.[!.]* ..?*будет соответствовать всем скрытым файлам, кроме .и..

.[!.]* ..?* *будет соответствовать всем файлам (скрытым или нет), кроме .и..

И чтобы ответить на конкретный пример этого вопроса вам нужно cd foo && mv .[!.]* ..?* * ../bar

объяснение

.[!.]*соответствует именам файлов, начинающимся с одной точки, за которой следует любой символ, кроме точки, за которой может следовать любая строка. Это достаточно близко, но оно пропускает файлы, начинающиеся с двух точек, например ..foo. Чтобы добавить такие файлы, мы добавляем ..?*совпадения с именами файлов, начинающимися с двух точек, за которыми следует любой символ, за которым может следовать любая строка.

Контрольная работа

Вы можете проверить эти шаблоны с помощью команд ниже. Я успешно пробовал их в bash и fish. Они терпят неудачу при sh, zsh, xonsh.

mkdir temp
cd temp
touch  a  .b  .bc  ..c  ..cd  ...d  ...de
ls .[!.]* ..?*
# you get this output:
          .b  .bc  ..c  ..cd  ...d  ...de
# cleanup
cd ..
rm -rf temp

0

Вы также можете найти и использовать обратные кавычки, чтобы выбрать файлы для команды перемещения. Передайте те в М.В.

Т.е. для скрытых файлов

find Foo -maxdepth 1 | egrep '^Foo/[.]' # Output: .hidden

Так

mv `find Foo -maxdepth 1 | egrep '^Foo/[.]'` Bar # mv Foo/.hidden Bar

Перемещает только выбранные скрытые файлы в Bar:

mv `find Foo -maxdepth 1 | egrep '^Foo/.'` Bar # mv Foo/.hidden Foo/notHidden Bar

Перемещает все файлы в Foo в Bar, начиная с '.' в команде egrep действует как подстановочный знак без квадратных скобок.

^Характер обеспечивает матч начинается с начала строки.

Некоторые подробности egrepсопоставления с образцом можно найти здесь .

Используя maxdepth 1стопы найдите от входа в подкаталоги.


0

Вдохновленный этим ответом :

Без копирования файлов ...

rsync -ax --link-dest=../Foo/ Foo/ Bar

Предостережения:

  • --link-destпуть должен быть абсолютным или относительным к DESTINATION ( Bar в примере)

  • Вы должны поставить /после SOURCE ( Foo/в примере), в противном случае он скопирует папку SOURCE вместо содержимого.


0

Я считаю, что это хорошо работает для Bash и нет необходимости изменять параметры оболочки

mv sourcedir/{*,.[^.]*} destdir/

РЕДАКТИРОВАТЬ:

Итак, как сказал G-man, мой первоначальный ответ не соответствует требованиям posix и во многом совпадает с ответом ndemou, приведенным выше, с одним изменением, которое заключается в использовании расширения скобок для создания списков строк, которые затем обрабатываются. Это просто означает, что вам не нужно cdзаходить в исходный каталог. Не очень большие изменения на самом деле, но они разные.

пример: допустим, у вас уже есть следующий макет.

 $ tree -a
.
├── destdir
└── sourcedir
    ├── ..d1
    ├── ..d2
    ├── ..double
    ├── file-1
    ├── file-2
    ├── .hidden-1
    ├── .hidden-2
    ├── ...t1
    └── ...t2

В первоначальном вопросе упоминались только скрытые файлы с одним периодом, но, скажем, есть некоторые с двумя или более периодами в начале имени. Вы можете просто добавить дополнительное выражение в фигурные скобки. Затем мы можем выполнить

mv sourcedir/{*,.[!.]*,..?*} destdir/

Это расширяется до следующего:

mv sourcedir/file-1 sourcedir/file-2 sourcedir/.hidden-1 sourcedir/.hidden-2 sourcedir/..d1 sourcedir/..d2 sourcedir/..double sourcedir/...t1 sourcedir/...t2 destdir/

Теперь вы должны увидеть все файлы, расположенные в destdir :

 $ tree -a
.
├── destdir
   ├── ..d1
   ├── ..d2
   ├── ..double
   ├── file-1
   ├── file-2
   ├── .hidden-1
   ├── .hidden-2
   ├── ...t1
   └── ...t2
└── sourcedir

Вы можете сделать несколько довольно крутых вещей с помощью фигурных скобок в bash с еще большим количеством дополнений в 4.x. Проверьте bash-хакеров для некоторых изящных примеров.


1
Это в основном дублирует ответ ndemou, за исключением того, что вы добавили фигурные скобки (не объясняя их). Но (1) ваш ответ не соответствует файлам, чьи имена начинаются с .., и (2) чтобы быть POSIX-совместимым , вы должны использовать !вместо ^; то есть  {*,.[!.]*}.
G-Man

@ G-Man, прости за это. Таким образом, вы правы, и я плохо, что не вижу ответа ndemou. Они в значительной степени одно и то же. 1. спасибо за указание на то, что моя версия не совместима с posix. 2. Я использую расширение скобки для создания списков строк, которые оболочка затем будет использовать для выполнения действий. Это также означает, что мне не нужно cdзаходить в исходный каталог, чтобы воздействовать на файлы или записывать одни и те же пути к файлам снова и снова, чтобы выразить пути ко всем файлам. Я вскоре обновлю пример, чтобы дать читателям более полное представление о том, о чем я говорю.
cmndr sp0ck

0

Для минимальных дистрибутивов Linux должно работать следующее. Сначала выполните базовый ход всех (при этом пропускаются скрытые файлы). Затем переместите все скрытые файлы (включая .и ..которые на самом деле не будут перемещены).

mv /sourceDir/* /destinationDir 2> /dev/null
mv /sourceDir/.* /destinationDir 2> /dev/null

Примечания. Если видимого содержимого нет, первая команда выдаст сообщение об ошибке. Вторая команда всегда выдаст ошибку о том, что она не может двигаться .и ... Таким образом, просто направьте ошибки в /dev/null(как показано).

Если вам нужно это как однострочный, просто объедините их точкой с запятой:

mv /sourceDir/* /destinationDir 2> /dev/null; mv /sourceDir/.* /destinationDir 2> /dev/null
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.