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

Вопрос или проблема

Я перемещаю свой домашний каталог со старой системы на новую, и созданный мной архив tarball содержит все, включая скрытые файлы, такие как .bashrc. Однако, когда я перемещаю содержимое распакованного архива tarball (которое находится в /tmp) в свой новый домашний каталог, скрытые файлы не копируются (mv /tmp/home/rcook/* /home/rcook/). Как мне заставить mv перемещать их?

На самом деле, я думаю, проблема не в mv, а в глоббировании bash. Если я делаю так:

mkdir a
mkdir b
touch a/.foo
touch a/bar
mv a/* b/
ls -a a/ b/

Я вижу это:

a/:
.  ..  .foo

b/:
.  ..  bar

a/.foo не переместился. Так как же заставить * находить скрытые файлы?

Да, я мог бы распаковать tarball напрямую в свой домашний каталог, но tarball распаковывается в home/rcook/..., и я хочу быть уверенным, что перезаписываю новый .bashrc и т.д. старыми, кастомизированными версиями, и знание, как находить и перемещать скрытые файлы, является полезным навыком. Предложения?


Некоторые ответы предполагают сделать что-то вроде mv src/.* dest/. Однако я попробовал это в своих тестовых каталогах и получил ошибки. Начав с:

rcook$ ls -a a/ b/
a/:
.  ..  bar  .foo

b/:
.  ..
rcook$ mv a/.* b/
mv: не удается переместить 'a/.' в 'b/.': Устройство или ресурс занят
mv: не удается удалить 'a/..': Это директория
rcook$ ls -a a/ b/
a/:
.  ..  bar

b/:
.  ..  .foo

Что я делаю неправильно?

Вы можете сделать следующее:

shopt -s dotglob
mv /tmp/home/rcook/* /home/rcook/

Вы можете поместить

shopt -s dotglob

в ваш ~/.bashrc, если хотите, чтобы это стало параметром по умолчанию.

См. http://mywiki.wooledge.org/glob


Другой подход для копирования точечных файлов:

mv /tmp/home/rcook/.[!.]* /home/rcook/

Не используйте паттерн ..*, так как он совпадает с .. (указатель на родительскую директорию). Если есть файлы, начинающиеся с двух точек (..something), также используйте паттерн ..?*.

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

mv src/* src/.* dst/

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

~/scratch [andrew] $ mv from/* from/.* to/
mv: не удается переместить ‘from/.’ в ‘to/.’: Устройство или ресурс занят
mv: не удается удалить ‘from/..’: Это директория
~/scratch [andrew] $ ls -a from/ to/
from/:
.  ..

to/:
.  ..  test  .test

Если вы ls -l в каталоге, вы увидите . и .. среди перечисленных файлов. Поэтому, я думаю, что mv .* /dest принимает во внимание эти указатели. Попробуйте:

mv /tmp/home/rcook/{*,.[^.]*,..?*} /home/rcook/

это проигнорирует эти указатели на текущие и родительские директории.

Вы получите ошибку, если какой-либо из трех паттернов *, [^.]* или ..?* не совпадет ни с одним файлом, поэтому включайте только те, которые совпадают.

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

cp -Rp . /desired/directory

затем удалить исходные файлы в текущем каталоге

Альтернативно, если вы знаете, что файлы имеют разумные названия (без пробелов, подстановочных знаков, непечатных символов), вы можете сделать что-то вроде этого

mv $(ls -A) /desired/directory

Я знаю, что это старо, но, один простой способ сделать это:

mv a/{.*,*} b/

Объяснение

Это называется расширением фигурных скобок. В этом случае оболочка расширяет путь, который вы указали, конкатенируя строку, данную перед фигурными скобками (в этом случае a/), с каждой вариацией, указанной в списке, окруженном фигурными скобками.
Список вариаций составлен из значений, разделенных запятыми и снова окруженных фигурными скобками. Таким образом, в этом случае у нас есть .* и * как элементы, так что список должен быть {.*,*}.

mv a/{.*,*} b/

Выражение выше можно прочитать следующим образом: переместите все файлы, начинающиеся с точки (.*) И все остальное (*) в директории a/ в директорию b/.

Больше советов

Это очень полезно и может быть использовано в различных сценариях. Например, представьте, что вам нужно создать 100 файлов с таким шаблоном именования: file1, file2, file3… file100. Вы можете легко добиться этого, сделав следующее

touch file{1..100}

В этом случае вы видите, что было задано диапазон. Вы можете сделать это, разделив первое и последнее значения двумя точками (..).

Переходя на следующий уровень!

Я новичок в мире Unix и, даже зная, что это работает в bash и zsh (оболочки, которые я использовал до сих пор), я не могу гарантировать, что это будет работать в любой оболочке.

Вы можете узнать больше о расширениях и глоббировании в документации вашей оболочки (используйте: man bash или man zshall для bash и zsh соответственно).

Надеюсь, это поможет будущим читателям!

На самом деле нет такого понятия, как “скрытые” файлы в Linux. Файлы, начинающиеся с точки, просто по умолчанию скрыты из списка файлов.

Чтобы скопировать файлы, даже с подстановкой, вам нужно добавить к файлу префикс ., такой как mv -u .* foo, и затем .foo появится как foo/.foo при перемещении.

Опция -u переместит файлы только в том случае, если исходный файл новее или если его нет в пункте назначения. Или вы можете просто игнорировать ошибки о перемещении . и .., так как это специальные файлы и их нельзя перемещать, но оболочка захватывает их в .*.

Так у нас есть:

a
|-- .foo
`-- bar

и:

b

Я потратил огромное количество времени, чтобы найти лучшее решение, чем все ответы здесь.

Затем я потратил 30 секунд на эту команду:

$ mv --help

Затем я нашел свою любовь:

$ mv a/ b/ -T

Примечание: я даже не понимаю аргумент -T. Он просто сделал то, что мне было нужно. Здесь сказано:

-T, –no-target-directory рассматривайте DEST как обычный файл

В моем случае я просто пытался mv одиночный скрытый файл и это было решением:

FILE=.myHiddenFile
mv "$FILE" dest/

Объяснение

Сначала я пытался:

mv .myHiddenFile dest/

Но все время выдавалась ошибка:

mv: cannot stat '.myHiddenFile': No such file or directory

Даже когда ls -al показывал, что файл и dest/ существовали.

Причина, видимо, в том, что оболочка интерпретировала “.” в имени файла, поэтому, перемещая его в переменную, вы избегаете интерпретации. Вероятно, есть и другие способы решения этой проблемы, но этого было достаточно.

Альтернативный способ – использовать (GNU) find:

find /tmp/home/rcook/ -mindepth 1 -maxdepth 1 -exec mv {} /home/rcook/ \;

Чтобы переместить все файлы и каталоги из текущей директории в ее
родительскую директорию, вы можете использовать

mv * ../

Примечание: Это не сработает, если вы хотите переместить скрытые файлы или каталоги,
Если вам нужно переместить скрытые файлы, используйте следующую команду.

mv * .[^.]* ../

Ответ или решение

При переносе файлов из временной директории в домашний каталог, важно учитывать, что команды и символы в Linux работают по определённым правилам, которые могут создавать неожиданные сложности. Например, обычное использование символа * не учитывает скрытые файлы, начинающиеся с точки, такие как .bashrc. Таким образом, при команде mv /tmp/home/rcook/* /home/rcook/ скрытые файлы не будут перенесены.

Чтобы переместить скрытые файлы, можно воспользоваться несколькими подходами:

1. Использование dotglob в bash:

При помощи команды shopt -s dotglob включается возможность учитывать скрытые файлы в результатах разворачивания. После этого команда mv /tmp/home/rcook/* /home/rcook/ переместит в том числе и файлы с точкой в начале. Если вы хотите, чтобы эта опция всегда была включена, добавьте её в ваш ~/.bashrc.

shopt -s dotglob
mv /tmp/home/rcook/* /home/rcook/

2. Использование альтернативных выражений для глобщения:

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

mv /tmp/home/rcook/{*,.[^.]*,..?*} /home/rcook/

Эта команда гарантирует, что все файлы включая скрытые, перенесутся корректно, избегая перемещения видимых и невидимых системных директорий . и ...

3. Использование команды find:

Команда find может быть более гибким способом переноса файлов:

find /tmp/home/rcook/ -mindepth 1 -maxdepth 1 -exec mv {} /home/rcook/ \;

Эта команда перемещает все файлы и директории (включая скрытые) из директории /tmp/home/rcook/ в /home/rcook/.

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

Оцените материал
Добавить комментарий

Капча загружается...