Переместить все файлы в подкаталогах в рабочий каталог и добавить путь в качестве префикса

#bash

#bash

Вопрос:

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

 maindir 
   - dir1 - dir12 -filea
                  -fileb
          - dir13 -dir131 - filea
                  -dir132 - filea
   - dir2 - filea
          - fileb
          - filec
   - dir3
 

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

 dir1_dir12_filea
dir1_dir12_fileb
.
.
dir2_filea
dir2_fileb
dir2_filec
.
.
 

и т. д

  for f in **/*
 do 
      dn=$(basename "$(dirname "$f")")
      bn=$(basename "$f")
      mv -- "$f" "${dn}_${bn}"
 done
 

но он проходит только через 1 уровень подкаталога в глубину

Комментарии:

1. ($basename должно быть $(basename

2. basename $(dirname $f) будет просто dir12 для dir1/dir12/filename , а не dir1_dir12 .

3. Похоже, что вы действительно хотите сделать, это заменить все / на _ in $f , чтобы создать цель.

Ответ №1:

Вы просто хотите заменить все / на _ in $f . Нет необходимости в dirname or basename .

 for f in **/*
do
    if ! [ -d "$f" ]
    then 
        dn=${f////_}
        mv -- "$f" "$dn"
    fi
done
 

Комментарии:

1. если я запущу это, я получу имена каталогов dir1_dir12, dir1_13 и т.д. в главном каталоге. Если я снова запущу скрипт, я получу dir1_dir12_filea в главном каталоге. Есть ли способ исправить это, чтобы он проходил через все подкаталоги при первом запуске?

2. Подстановочный знак соответствует как файлам, так и каталогам. Используется if для фильтрации каталогов.

Ответ №2:

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

 find * -mindepth 1 -type f | sed -n 'p;s@/@_@gp' | sed 'N;s@n@ @' | xargs -n2 mv
 

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

 find * -mindepth 1 -type f | sed -n 'p;s@/@_@gp' | sed 'N;s@n@ @' | xargs -n2 echo mv
 

Это просто повторит все команды, которые будут выполнены. Если вас это устраивает, запускайте без echo.


Объяснение:

Первое sed выполнение:

 sed -n 'p;s@/@_@gp'
 

При этом выполняются следующие операции над каждой строкой в следующем порядке,

  1. p : распечатать текущую строку как есть
  2. s@/@_@gp : замените все / символы входной строки _ символами и выведите их (в следующей строке). g (глобальный) флаг предназначен для выполнения подстановки для всех символов. В противном случае, если существует после первой замены.

Теперь у нас есть две строки вывода для одного ввода.

Например.

Если первый find результат есть root/dir/file1 , нам нужно сгенерировать следующую команду,

 mv root/dir/file1 root_dir_file1
 

Следующие две строки появляются в конце первого sed канала

 root/dir/file1
root_dir_file1
 

Если эти две строки объединяются вместе с пробелом, мы получаем аргументы mv команды. Второй sed только для этого.

Второе sed выполнение:

 sed 'N;s@n@ @'
 

При этом выполняются следующие операции над каждой строкой в следующем порядке,

  1. N : Соединить текущую строку ввода (строку в пространстве шаблонов) со следующей строкой ввода с символом новой строки между ними.
  2. s@n@ @ : Удалить символ новой строки с пробелом

Теперь у нас есть необходимые mv аргументы. xargs делает все остальное.

Комментарии:

1. Не могли бы вы объяснить, что делает каждая часть sed? Спасибо!

2. @Barmarесли производительность вызывает беспокойство, возможно, вы правы. Но обычно это одноразовые задания, где производительность не вызывает беспокойства. В этом случае я предпочитаю простой однострочник, который просто выполняет работу, а не пишет скрипт. Я просто поделился тем, что я бы сделал. Это зависит от OP, чтобы решить, что лучше для него. 🙂

3. Кстати, мне было интересно, есть ли способ заменить два sed вызова одним. Есть ли способ объединить pattern и hold разместить содержимое без новой строки между ними?

4. В общем, использование sed для выполнения простых замен в переменных — это техника 1990-х годов, поскольку теперь вы можете использовать встроенный bash ${var//old/new} . Это не только повышает производительность, но и упрощает код.

5. Разве вы не можете выполнить две sed операции с sed -n -e 'p;s@/@_@g' -e 'N;s@n@ @p'