Как переместить каталог в каталог с таким же именем?

Значение всех опций написано в справочной странице:

-R, --randomwait=[time in minutes]
    Sets the maximum amount of time yum will wait before performing a command - 
    it randomizes over the time. 

-d, --debuglevel=[number]
    Sets the debugging level to [number] - turns up or down the amount of things 
    that are printed. Practical range: 0 - 10

-e, --errorlevel=[number]
    Sets the error level to [number] Practical range 0 - 10. 0 means print only critical 
    errors about which you must be told. 1 means print all errors, even ones that are 
    not overly important. 1+ means print more errors (if any) -e 0 is good for cron jobs.

Итак, /usr/bin/yum -y -R 120 -d 0 -e 0 update yumобновляет пакет yumпосле произвольного ожидания до 2 часов, выводя только критические ошибки.

32
01.12.2019, 15:15
9 ответов

У вас уже почти все получилось. Вы можете выбрать другое имя для переходного каталога, например, целевое имя с текущей датой/временем в наносекундах и наш PID в качестве составного суффикса, но это по-прежнему предполагает, что каталог еще не существует :

.
dir=foo                         # The directory we want to nest
now=$(date +'%s_%N')            # Seconds and nanoseconds
mkdir "$dir.$now.$$"            # Create a transient directory
mv -f "$dir" "$dir.$now.$$/"    # Move our directory inside transient directory
mv -f "$dir.$now.$$" "$dir"     # Rename the transient directory to the name with which we started

Если вам нужно гарантированно надежное решение, я бы перебирал mkdirдо тех пор, пока оно не было успешным

now=$(date +'%s_%N')
while ! mkdir -m700 "$dir.$now.$$" 2>/dev/null
do
    sleep 0.$$
    now=$(date +'%s_%N')
done
6
27.01.2020, 19:37

Чтобы безопасно создать временный каталог в текущем каталоге с именем, которое еще не занято, вы можете использовать mktemp -dвот так:

tmpdir=$(mktemp -d "$PWD"/tmp.XXXXXXXX)   # using./tmp.XXXXXXXX would work too

Команда mktemp -dсоздаст каталог по указанному пути, заменивX-es в конце пути на случайные буквенно-цифровые символы. Он вернет путь к созданному каталогу, и мы сохраним это значение в tmpdir. 1

Эту переменную tmpdirзатем можно использовать при выполнении той же процедуры, которую вы уже выполняете, с заменой barна"$tmpdir":

mv foo "$tmpdir"
mv "$tmpdir" foo
unset tmpdir

unset tmpdirв конце просто удаляет переменную.


1Обычно один должен иметь возможность установить переменную среды TMPDIRна путь к каталогу, в котором нужно создать временные файлы или каталоги с помощью mktemp, но утилита в macOS похоже, что эта утилита работает несколько иначе, чем та же утилита в других системах BSD, и создаст каталог в совершенно другом месте. Вышеупомянутое, однако, будет работать на macOS. Использование немного более удобного tmpdir=$(TMPDIR=$PWD mktemp -d)или даже tmpdir=$(TMPDIR=. mktemp -d)было бы проблемой только в macOS, если временный каталог по умолчанию находился в другом разделе, а каталог fooсодержал много данных (, т.е. это было бы медленно ).

22
27.01.2020, 19:37

Я предлагаю наоборот. Не перемещайте каталог, а только его содержимое:

.
└── foo
    ├── a.txt
    └── b.txt
mkdir foo/foo
.
└── foo
    ├── foo
    ├── a.txt
    └── b.txt
cd foo
mv $(ls | grep -v '^foo$') foo
cd -
.
└── foo
    └── foo
        ├── a.txt
        └── b.txt

Если у вас есть bash, вы также можете

shopt -s extglob
cd foo
mv !(foo) foo
cd -

(как описано здесь ), чтобы избежать запуска ls и grep.

Преимущество этого решения в том, что оно очень простое.

Недостатки (как указано в комментариях):

  • Не удается обработать скрытые файлы(ls -Aисправляет, но неls -a)
  • Не удается обработать имена файлов, содержащие пробелы (можно исправить с помощью кавычек:mv "$(ls | grep -v '^foo$')" foo
  • Не удается обработать имена файлов, содержащие символы новой строки и другие специальные символы

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

8
27.01.2020, 19:37

В macOS вы можете установить renameкоманду (Perl-скрипт )с помощью Homebrew:

brew install rename

Затем с помощью-p(а-ляmkdir)создайте необходимые каталоги и -Aдобавьте префикс:

% mkdir -p foo/bar; touch foo/{a,b}.txt foo/bar/c.txt
% rename -p -A foo/ foo/*
% tree foo
foo
└── foo
    ├── a.txt
    ├── b.txt
    └── bar
        └── c.txt

Прогон с -nдля отображения изменений без переименования (пробный прогон -прогон):

% rename -p -A foo/ foo/* -n
'foo/a.txt' would be renamed to 'foo/foo/a.txt'
'foo/b.txt' would be renamed to 'foo/foo/b.txt'
'foo/bar' would be renamed to 'foo/foo/bar'

Если у вас точечные файлы, так что простой *их не подхватит, то используйте другие методы сrename:

  • С помощью bash (mis )используйте GLOBIGNORE, чтобы получить *для соответствия файлам точек:

    $ GLOBIGNORE=.; rename -p -A foo/ foo/* -n
    'foo/.baz' would be renamed to 'foo/foo/.baz'
    'foo/a.txt' would be renamed to 'foo/foo/a.txt'
    'foo/b.txt' would be renamed to 'foo/foo/b.txt'
    'foo/bar' would be renamed to 'foo/foo/bar'
    
  • Или используйте findс -print0и renameс-0:

    % find foo -mindepth 1 -maxdepth 1 -print0 | rename -0 -p -A foo/ -n
    Reading filenames from STDIN
    Splitting on NUL bytes
    'foo/b.txt' would be renamed to 'foo/foo/b.txt'
    'foo/a.txt' would be renamed to 'foo/foo/a.txt'
    'foo/bar' would be renamed to 'foo/foo/bar'
    'foo/.baz' would be renamed to 'foo/foo/.baz'
    
10
27.01.2020, 19:37

Пока содержимого недостаточно, чтобы превысить максимальные пределы параметров, (и вы не возражаете против «приемлемого» сообщения об ошибке ), тогда это не должно быть более сложным, чем это:

mkdir foo/foo
mv foo/* foo/foo

Поправка для обработки скрытых файлов:

mkdir foo/foo
mv foo/{.,}* foo/foo
10
27.01.2020, 19:37
mkdir foo/foo && mv foo/!(foo) foo/foo

Вам нужно перейти в каталог, где находится исходная папка (foo ).

Затем выполните приведенную выше команду. Он создаст папку с тем же именем и переместит содержимое родительского foo в дочерний каталог foo (, за исключением дочернего каталога, поэтому ! обозначение ).

Если дочерний каталог foo уже существует, вы можете игнорировать первую часть и просто запустить команду mv:

mv foo/!(foo) foo/foo

В MacOS вам может потребоваться установить параметр extglob:

shopt -s extglob
6
27.01.2020, 19:37

В дополнение к приведенным выше предложениям вы можете проверить rsync. Прежде чем я начну, всегда не забывайте использовать параметр --dry-runс rsync, прежде чем запускать его без него. --dry-runрасскажет вам, что произошло бы в реальной жизни.

У rsync есть множество опций, которые могут не только помочь копировать/переместить файлы, но и ускорить процесс. Вот один из способов выполнить свою работу. Опция --remove-source-filesудаляет файлы из источника после процесса перемещения (, который фактически является копией, за которой следует удаление ). Обратите внимание, что команда rsync сначала считывает содержимое foo, создает каталог foo в существующем каталоге с именем foo, а затем запускает процесс копирования. Это заканчивается удалением исходных файлов в foo. Предостережение :Обратите особое внимание на символы косой черты в именах каталогов.

Есть и другие соображения, на которые многие указывали выше (, такие как символические ссылки ), но man rsyncпоможет вам выбрать нужные параметры. Примечание здесь :Поскольку вы просили решение для файлов, это сработает. Это не удалит пустые каталоги, которые останутся позади. Я не знаю, как это сделать без дополнительного шага, поэтому, если кто-то может что-то добавить, заранее спасибо!

mkdir foo
cd foo
touch aaa bbb ccc ddd
cd..
rsync -av --remove-source-files foo foo/

a = режим архива

v = подробный

2
27.01.2020, 19:37

Просто не трогайте foo, и у вас не будет проблем с одинаковыми именами. Вместо этого создайте внутри него каталог с таким же именем и переместите все туда. Используйте трюк $ _для этого и &&, чтобы сделать его одним -лайнером:

cd foo && mkdir $_ && mv * $_

Это вызовет безобидную ошибку.(mv :переименовать foo в foo/foo :Недопустимый аргумент ), который можно игнорировать.

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


вы можете смешать это с другими ответами для еще большего эффекта:

  • mv {.,}* $_позаботится о скрытых файлах (за счет большего количества ошибок, которые вы также можете игнорировать)
  • mv./!(foo)позволит избежать сообщения об ошибке, но только если вы установитеshopt -s extglob
2
27.01.2020, 19:37

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

$ mkdir foo
$ mkdir bar
$ mkdir./bar/foo
$ touch./bar/foo/file1 && touch./bar/foo/file2 && touch./bar/foo/file3
$ ls./
bar/ foo/
$ ls./bar/foo
file1 file2 file3

А теперь волшебная часть

$ mv./bar/foo./foo/
$ ls./foo/
foo/
$ ls./foo/foo/
file1 file2 file3

Теперь, почему это работает? Я не могу дать вам самый точный ответ, так как я всего лишь новичок -, но я понимаю, что все сводится к тому, как конечные /символы обрабатываются в папках.

Посмотрите внимательно на эту команду mv

$ mv./bar/foo./foo/

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

Мне жаль, что я не могу предложить более развернутый ответ, чем этот, но, возможно, кто-то более свободно осведомленный возьмет на себя смелость отредактировать мой ответ.

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

Надеюсь, вам это поможет!

0
27.01.2020, 19:37

Теги

Похожие вопросы