1

У меня есть каталог, заполненный различными файлами, с разными именами (то есть без имен с определенным шаблоном) и с разными расширениями, и я хочу поместить каждый файл во вновь созданный подкаталог, названный в честь каждого файла; Каков наилучший способ сделать это из командной строки под Linux?
Это звучит немного запутанно, позвольте мне привести пример: давайте вызовем случайный файл в каталоге ./filename.ext , я бы хотел поместить его в подкаталог с именем ./filename чтобы новый путь был ./filename/filename.ext .

Я сделал несколько испытаний со следующими шагами:

  • Я создал несколько тестовых файлов, от file-1.txt до file-9.txt , внутри тестового каталога с параметром for num in {1..9}; do touch file-$num.txt; done
  • Я создал каталог с именем каждого файла с for name in $(eval ls | cut -b 1-6); do mkdir $name; done
  • Я переместил исходные файлы в соответствующие каталоги с for name in $(eval ls | grep -e ".txt" | cut -b 1-6); do mv $name.txt $name/$name.txt; done

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

В качестве бонуса, это было бы здорово , чтобы найти способ , чтобы создать подкаталоги автоматически при перемещении / переименование файлов, без необходимости использовать mkdir ранее (поэтому , когда я ls на самом деле переименовать, я не придется беспокоиться об исключении вновь созданные каталоги); но в конце концов это вторичная проблема.

2 ответа2

2

Я думаю, что решение вашей проблемы называется заменой параметров (посмотрите подробное описание). Учитывая имя файла в filename вы получите его расширение, используя ${filename##*.} И имя без расширения с ${filename%.*}

Тем не менее, вы можете изменить свой цикл for следующим образом:

# for all (*) files in the current directory
for i in *;
do
    # -f checks if it is a file (skip directories)
    [ -f "$i" ] || continue
    # store the file name in filename
    filename=$(basename $i)
    # ext = the extension of the file (here we don't care, jfi)
    ext="${filename##*.}"
    # dir = the file name without its extension
    dir="${filename%.*}"
    # create the directory
    mkdir -p $dir
    # move the file in that directory
    mv $i $dir
done

К сожалению, я не получил очко за ваше бонусное задание, поэтому оно останется открытым испытанием ... :)

1

Вот подход с меньшим количеством скриптов, но с большим количеством команд.

   find -maxdepth 1 -type f > file-list

Найдите все файлы в текущем каталоге и сохраните этот список.

   grep -o '\./.*\.' file-list | sed s/\.$// > dir-list

Фильтруйте этот список, чтобы включить только те имена файлов, которые где-то имеют точку. Удалите эту последнюю точку с помощью sed, сохраните этот список.

   mkdir $(<dir-list)

Создайте все папки.

   sort dir-list > dir-list.s
   sort file-list > file-list.s

Это для выравнивания (по порядку в списке) имен файлов с именами папок>

   paste file-list dir-list

Это будет выглядеть неправильно, потому что файл file-list будет здесь. В моем случае был также файл с именем random. Удалите эти имена из списка файлов.

   sed -E '/^\.\/(random|file-list)$/d' -i file-list
   sort file-list > file-list.s

Исследуйте вывод следующей команды еще раз: вставьте dir-list списка файлов

Это должно выглядеть так:

   ./CCrtWW].GdH   ./CCrtWW]
   ./c[ifzJlKnEYXO.wXF ./c[ifzJlKnEYXO
   ./FAEhz.u[A ./FAEhz
   ./FGMlrKcJHF.pHp    ./FGMlrKcJHF
   ./HGxKPWZK.MpK  ./HGxKPWZK
   ./JxwNrN.zoj    ./JxwNrN

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

 paste file-list dir-list | sed 's/^/mv -v /' | sh

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .