копирование структуры каталогов с помощью сценариев оболочки - PullRequest
2 голосов
/ 13 января 2012

У меня есть структура каталогов на моем компьютере, как указано ниже

 bhagwat        durga    Sai Baba    vishnu sahastranam
  bhagwat geeta  hanuman  ramayan audio  shiv      vishnu

, в этом все перечисленные выше каталоги, в именах которых есть пробелы.т.е. если вы сделаете ls -l, вы увидите следующее

drwx------ 1 deel deel        0 2011-09-12 21:34 bhagwat
drwx------ 1 deel deel     8192 2011-09-09 22:35 bhagwat geeta
drwx------ 1 deel deel     4096 2011-10-07 05:10 durga
drwx------ 1 deel deel        0 2011-10-29 09:23 hanuman
drwx------ 1 deel deel     8192 2011-09-30 22:48 ramayan audio
drwx------ 1 deel deel     4096 2011-09-18 12:16 Sai Baba
drwx------ 1 deel deel     4096 2011-09-26 19:19 shiv
drwx------ 1 deel deel     4096 2011-09-26 19:20 vishnu
drwx------ 1 deel deel     4096 2011-09-16 11:35 vishnu sahastranam

, в котором есть дополнительные подпапки. Я НЕ хочу копировать файлы или каталоги.Я хочу создать структуру каталогов, как указано выше, в каком-то другом месте или устройстве. Что аналогично тому, что существует в исходном каталоге.Поэтому я пишу сценарий для того же, что и ниже

#!/bin/bash
for i in `ls /media/New\ Volume/bhajans`;do
echo $i 
done

Теперь проблема этого подхода заключается в том, что он приводит к следующему выводу

bhagwat
bhagwat
geeta
durga
hanuman
ramayan
audio
Sai
Baba
shiv
vishnu
vishnu
sahastranam

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

РЕДАКТИРОВАТЬ
Я использую Ubuntu 11.04
ОБНОВЛЕНИЕ
У каждого из клонированных подкаталогов есть скрипт с именем script.sh, поэтому я не только клонирую структуру каталогов. Я также копируюscript.sh из родительских каталогов в клонированные места назначения.Кевину и Джонатану спасибо за ваши объяснения и ответы.

Ответы [ 6 ]

4 голосов
/ 13 января 2012

Для клонирования структуры каталогов (рекурсивно) из olddir в newdir:

find olddir -type d -printf "newdir/%P\0" | xargs -0 mkdir -p

Если вы хотите только первый уровень, добавьте -maxdepth 1 к find.

Пояснение:

find, конечно, рекурсивно ищет каталог и действует на найденные файлы. olddir говорит ему искать в каталоге с именем "olddir". -type d указывает, что он должен действовать только на каталоги. -printf говорит ему напечатать следующий шаблон: newdir - это каталог, в который вы хотите клонировать структуру. %P печатает имя файла без части olddir, а \0 говорит ему завершать имя нулевым символом. Этот нулевой символ позволит нам передать любое допустимое имя файла в xargs. xargs выполняет команду, которую вы ей даете, используя то, что он читает из stdin в качестве аргументов. -0 говорит ему использовать нулевой байт (\0), чтобы определить, где следует разделять аргументы (в нашем случае имена файлов). В противном случае он использовал бы пустое пространство, и мы знаем, что не хотим, чтобы это делалось. -p говорит mkdir создать родительские каталоги, если они еще не существуют.

ОК, копировать каждый файл немного сложнее, но я уверен, что это будет работать во всех случаях (по крайней мере, во всех случаях, когда установлена ​​совместимая команда [gnu] find) после того, как вы клонировали структуру с помощью команда выше:

$ find olddir -name script.sh -printf "%p\0" -printf "newdir/%P\0" | xargs -0L2 cp -n

Первый printf печатает исходный (существующий) файл (%p), второй печатает конечный файл. Оба заканчиваются в \0, как мы делали ранее. Новый аргумент xargs, -L2 говорит xargs брать по два (2) аргумента (файла) за раз из его ввода и заставлять каждую команду выполняться только с двумя из них. Так что порядок операторов printf и L2 очень важен. -n говорит cp не перезаписывать пункт назначения, если он существует, на тот случай, если один из нас допустил ошибку.

2 голосов
/ 13 января 2012

Попробуйте

find . -maxdepth 1 -type d | xargs -I X mkdir '/new/directory/X'

Извините за первоначальную ошибку.

1 голос
/ 13 января 2012

Как вы заметили, проблема состоит в том, что bash обрабатывает пробелы в именах каталогов как разделитель и выполняет итерацию по каждому слову в отдельности.«Правильным» решением было бы сказать ему не делать этого, но в отсутствие этого вот глупый обходной путь!

#!/bin/bash
for i in `ls -F | grep "/$" | sed s/\ /###/g`;do
    echo $i | sed s/"###"/\ /g
done

Прежде всего, часть ls -F | grep "/$" фильтрует ваши результаты ls ввозвращать только каталоги, а не файлы (или символические ссылки).Затем команда sed преобразует пробелы в именах каталогов в строку-заполнитель ("###"), которая больше не ошибается в качестве разделителя.Внутри цикла команда reverse sed переводит их обратно в пробелы.

Конечно, вы можете заменить "###" на любые символы, которые вам не нужны.

Теперь, чтобы использовать это для создания каталогов:

#!/bin/bash
for i in `ls -F | grep "/$" | sed s/\ /###/g`;do
    tempname=`echo $i | sed s/"###"/\ /g`
    mkdir "otherlocation/$tempname"
done

Просто замените "otherlocation" на путь, который вы хотите использовать для новых каталогов.

1 голос
/ 13 января 2012

Если это должен быть сценарий оболочки, он немного грязный. Если это может быть скрипт, запускаемый из оболочки, тогда Perl или Python могут лучше служить вашим целям.

Ответ пара 2

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

Shell

Предполагая GNU find и xargs; POSIX find не поддерживает -print0 или -printf, а также POSIX xargs не поддерживает -0.

(cd "/media/New\ Volume/bhajans"; find . -maxdepth 1 -type d -print0) |
     xargs -0 mkdir -p

Это гарантирует, что имена, перечисленные в find, настолько просты, насколько это возможно (./name1 и т. Д.), Но вывод включает в себя . и mkdir . не удается, поскольку каталог существует. Использование mkdir -p останавливает это как ошибку.

Обратите внимание, что -print0 сильно отличается от -print 0. Первая - это специальная команда, вариант команды -print для find. Вместо завершения имени пробелом или новой строки, оно завершает его с помощью ASCII NUL. Это умное расширение от GNU. Есть только два символа из 256 возможностей, которые не могут отображаться в имени файла в каталоге. Одним из них является /, потому что он используется в качестве разделителя пути; другой - ASCII NUL. Завершая имена с помощью ASCII NUL (называемого '\0' в коде C; у него нулевое значение байта), имена однозначно отделяются друг от друга. Это, однако, требует программ, которые ожидают, что эта конвенция будет работать; Например, xargs пришлось изменить, чтобы его можно было распознать.

Когда вы написали -print 0, вы сказали find напечатать имена, а затем добавили еще один каталог (названный 0) в список, который он должен был искать, и он возразил против этого.

Когда вы писали -printf 0, вы говорили find находить для печати по одному 0 каждый раз, когда он находил файл, так оно и было. Он не добавлял символы новой строки или пробелы (вы не просили об этом), поэтому xargs увидел строку, содержащую 00000000, а затем создал ее как подкаталог, в точности так, как вы его просили.

* * Perl тысяча сорок-девять

Это становится, во всяком случае, немного легче:

use strict;
use warnings;
my $otherdir = $ARGV[0];

die "$otherdir is not a directory" unless -d $otherdir;

opendir(my $dir, $otherdir) or die "failed to open directory $otherdir";
while (my $name = readdir($dir))
{
    next if $name eq "." || $name eq "..";
    mkdir $name if (-d $file);
}
closedir($dir);

Это просто читает имена из удаленного каталога и создает эти каталоги в текущем каталоге.

Ответ пара 1

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

Shell

Предполагая GNU find и xargs; POSIX find не поддерживает -print0 или -printf, а также POSIX xargs не поддерживает -0.

find . -maxdepth 1 -type d -print0 |
    (cd /SomeWhere/Else; xargs -0 mkdir)

Perl

use strict;
use warnings;
my $otherdir = $ARGV[0];

die "$otherdir is not a directory" unless -d $otherdir;

opendir(my $dir, ".") or die "failed to open current directory";
while (my $name = readdir($dir))
{
    mkdir "$otherdir/$name" if (-d $file);
}
closedir($dir);

Официально не проверено.

1 голос
/ 13 января 2012

Попробуйте что-то вроде этого -

while IFS=$\n read -r i; do 
    echo "$i"; 
done < <(ls -1 /media/New\ Volume/bhajans)
0 голосов
/ 06 августа 2013

Вы можете попробовать

#!/bin/bash
for i in ls /media/New\ Volume/bhajans/* ;do
echo $i 
done
...