Так, у меня есть структура каталогов как это:
parent/
├── sub1
│ └── source
│ └── file1
│ └── file2
├── sub2
│ └── sub2.1
│ └── source
│ └── something1
│ └── something2
└── sub3
└── sub3.1
└── sub3.1.1
└── source
└── other.zip
Я хочу переместить все файлы (с другим именем файла) из всех названных каталогов source
к его относительному верхнему / родительскому каталогу. Так, результат должен быть чем-то вроде этого:
parent/
├── sub1
│ ├── file1
│ ├── file2
│ └── source
├── sub2
│ └── sub2.1
│ ├── something1
│ ├── something2
│ └── source
└── sub3
└── sub3.1
└── sub3.1.1
├── other.zip
└── source
Есть ли простой способ (один лайнер) для выполнения этого, возможно, с помощью find
команда? Предпочтительно один это не слишком сложно, чтобы я понял.:D Я довольно плохо знаком с Linux.
Править: Я также собираюсь сделать сценарий удара (таким образом, я могу использовать его легко) из решения. Например: ./movefiles.sh myfolder
Так, предпочтительно, решение может легко разместить, umm переменные?, особенно, которые имеют символы как .
(если это - скрытый каталог), #
, @
, и т.д.
Если Вы хотите a find
решение, Вы могли использовать это:
find parent -name "source" -type d -exec bash -c 'cd "$1"; mv * ..' bash {} \;
Объяснение:
find parent -name "source" -type d
- Для каждого названного каталога source
в parent
...-exec bash -c '...' bash {} \;
- Назовите Bash с bash
как $0
и путь к каталогу как $1
cd "$1"; mv * ..
- CD в каталог; переместите все его содержание один уровень. mv "$1"/* "$1/.."
Это более или менее основано на ответе десерта.
find parent -name "source" -type d -exec bash -c 'shopt -s dotglob; cd "$1"; mv * ..' bash {} \;
shopt -s dotglob
- Заставьте шарики включать скрытые файлыПростое for
цикл с globstar
опция включила (выполненный shopt -s globstar
чтобы сделать это), сделает задание:
dirname="source"
for i in ./**/"$dirname"/; do
mv "$i"* "${i%$dirname/}"
done
GNU parallel
эквивалентный для этого цикла:
parallel mv {}* {//}/ ::: ./**/"$dirname"/
Полностью другой подход должен использовать rename
для удаления “$dirname /” расстаются со строкой:
rename "s/$dirname\///" **/"$dirname"/*
Заключение в кавычки с двойными кавычками, как я сделал здесь, сохраняет значение каждого специального символа кроме $
, `
, \
и !
. Если Вы хотите включать “скрытые” точечные файлы в какое-либо из вышеупомянутых решений, установите dotglob
опция прежде, чем выполнить его: shopt -s dotglob
globstar
Если установлено, шаблон**
используемый в контексте расширения пути будет соответствовать всем файлам и нулю или большему количеству каталогов и подкаталогов. Если шаблон сопровождается a/
, только каталоги и соответствие подкаталогов.
./**/source/
соответствует каждому названному каталогу source
в и под текущим каталогом, mv
управляйте перемещает каждый файл из каталога к его родительскому каталогу – ${i%source/}
расширение параметра, которое удаляет строку source/
от конца (путь) строка.
$ tree
.
├── sub1
│ └── source
│ ├── file1
│ └── file2
├── sub2
│ └── sub2.1
│ └── source
│ ├── something1
│ └── something2
└── sub3
└── sub3.1
└── sub3.1.1
└── source
└── other.zip
$ dirname="source"
$ for i in ./**/"$dirname"/; do mv "$i"* "${i%$dirname/}"; done
$ tree
.
├── sub1
│ ├── file1
│ ├── file2
│ └── source
├── sub2
│ └── sub2.1
│ ├── something1
│ ├── something2
│ └── source
└── sub3
└── sub3.1
└── sub3.1.1
├── other.zip
└── source
Можно использовать ниже кода для получения то, что Вы хотите:
dir=$(find . -name 'source' | sed s:source::)
for path in $dir; do
mv "$path"source/* "$path"
done
find
управляйте возвращает путь к каталогу от родителя до исходного каталога. В этом find . -name 'source'
'.' представляет родительский каталог, и 'источник' представляет подкаталог, который Вы хотите найти.
sed
команда удаляет source
от результата find
команда.
И остальное - просто повторение (for
) и переместите команду (mv
)
Для этих видов задач, где могли бы быть неожиданности, одним из лучших путей является "сценарий сценария". Мы выполняем команду, которая производит сценарий, обычно очень повторяющийся, чтобы сделать задачу. После того как мы удовлетворены, что это корректно, мы передаем это по каналу через sh
выполнять его. Это превращает сложную проблему в намного более простую проблему редактирования, и это - чрезвычайно общая техника, применимая ко всем видам проблем, не только этой трудной move-some-files-upstairs проблемы. Это имеет преимущество не использования любых экзотических конструкций оболочки и так будет работать в каждой оболочке (чистый Posix sh
, bash
, csh
, и так далее). Поскольку Вы видите все основные команды, прежде чем они будут выполнены, это - хороший пример "look-before-you-leap".
Сначала найдите все каталоги, которые мы собираемся изменить:
$ find . -type d -name source
Это дает
./sub3/sub3.1/sub3.1.1/source
./sub2/sub2.1/source
./sub1/source
Затем для каждого из тех каталогов мы хотим переместить содержание один каталог, мы думаем о команде, которую мы выполнили:
$ mv $dir/* $dir/..
Мы используем awk
(или sed
или безотносительно) для записи команд. Таким образом, мы передаем список по каналу каталогов в awk:
$ find . -type d -name source \
| awk '{printf("mv %s/* %s/..\n", $0, $0);}'
mv ./sub3/sub3.1/sub3.1.1/source/* ./sub3/sub3.1/sub3.1.1/source/..
mv ./sub2/sub2.1/source/* ./sub2/sub2.1/source/..
mv ./sub1/source/* ./sub1/source/..
Мы можем делать это так же много раз по мере необходимости, редактируя тщательно, пока мы не видим, что команды корректны.
Затем мы передаем все по каналу это через sh
на самом деле сделать это:
$ find . -type d -name source \
| awk '{printf("mv %s/* %s/..\n", $0, $0);}' \
| sh
Часто хороший, чтобы иметь окончательный результат говорят, что он делает, и выход на первой ошибке, так использование sh -e -x
:
$ find . -type d -name source \
| awk '{printf("mv %s/* %s/..\n", $0, $0);}' \
| sh -e -x
Если Вы не уверены относительно awk
для этого редактирования можно сделать это с sed
или чистый find
find . -type d -name source | sed 's|\(.*\)|mv \1/* \1/..|' # sed
find . -type d -name source -exec echo 'mv {}/* {}/..' ';' # find/echo
Вложенная команда находки сделает задание в одной строке, но это может казаться немного изворотливым.;-)
Конкретно:
find . -name source -type d -exec sh -c 'find "$0" -type f -execdir mv "{""}" .. \;' {} \;
find . -name source -type d
находит все каталоги названными source
в токе (.
) каталог.
-exec sh -c '...' {} \;
выполняет a sh
с командой '...'
для каждого найденного каталога, включает имя каталога {}
, и кроме того определяет выполняемый sh
переменная $0
с содержанием {}
(см. опцию -c
в sh
страница справочника)
аргумент sh -c
, 'find "$0" -type f ... '
находит все файлы в рамках каталога $0
и наконец ... -execdir mv "{""}" ..
перемещает найденные файлы {}
(здесь скрытый как "{""}"
предотвратить замену из-за восходящего потока -exec
) к (локальный для каталога - поэтому -execdir
) родительский каталог ..
marcus@gargabuntu:~/temp$ tree
.
├── sub1
│ └── source
│ ├── file1
│ └── file2
├── sub2
│ └── sub2.1
│ └── source
│ ├── something1
│ └── something2
└── sub3
└── sub3.1
└── sub3.1.1
└── source
└── other.zip
9 directories, 5 files
marcus@gargabuntu:~/temp$ find . -name source -type d -exec sh -c 'find "$0" -type f -execdir mv "{""}" .. \;' {} \;
marcus@gargabuntu:~/temp$ tree
.
├── sub1
│ ├── file1
│ ├── file2
│ └── source
├── sub2
│ └── sub2.1
│ ├── something1
│ ├── something2
│ └── source
└── sub3
└── sub3.1
└── sub3.1.1
├── other.zip
└── source
9 directories, 5 files
Простой
dir='source'
for i in `find . -name "*" | grep $dir/*| grep -v "$dir$"`; do
mv "$i" "${i%$dir/*}"
done
Тест
$ cd Desktop/test/
/Desktop/test$ tree
.
├── sub1
│ └── source
│ ├── file1
│ └── file2
├── sub2
│ └── sub2.1
│ └── source
│ ├── something1
│ └── something2
└── sub3
└── sub3.1
└── sub3.1.1
└── source
└── other.zip
9 directories, 5 files
/Desktop/test$ dir='source'
/Desktop/test$ for i in `find . -name "*" | grep $dir/*| grep -v "$dir$"`; do
> mv "$i" "${i%$dir/*}"
> done
/Desktop/test$ tree
.
├── sub1
│ ├── file1
│ ├── file2
│ └── source
├── sub2
│ └── sub2.1
│ ├── something1
│ ├── something2
│ └── source
└── sub3
└── sub3.1
└── sub3.1.1
├── other.zip
└── source
9 directories, 5 files
/Desktop/test$