1

У меня есть коллекция каталогов, которые все содержат определенный файл, который называется одинаково во всех каталогах.

dir1/dirA/file.txt
dir1/dirB/file.txt
dir1/dirC/file.txt
....
dir4/dirX/file.txt
dir4/dirY/file.txt
dir4/dirZ/file.txt

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

all_files/
    dirA.txt
    dirB.txt
    dirC.txt
    ....
    dirX.txt
    dirY.txt
    dirZ.txt

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

Я использую Bash в OS X. Я попытался сделать несколько вещей, как это (см. Здесь и здесь). Я хотел бы разбить строку на слешах, получить от второго до последнего и использовать это, но я не смог заставить его работать (основываясь на этом и других):

for name in `ls */*/file`
    directory=${DIRS[${#DIRS[@]} - 2]}
    mv name ../all_files/${directory}.txt
done

Однако я не мог заставить его работать.

2 ответа2

2
for name in ./*/*/file.txt
do
    mv "$name" "../all_files/$(basename -- "$(dirname -- "$name")").txt"
done

Некоторые комментарии:

  • Никогда не делай этого или что-то подобное:

    for name in `ls */*/file`
    

    Если в именах файлов есть пробелы или другие сложные символы, вышеописанное не будет выполнено. Вместо этого используйте:

    for name in ./*/*/file
    

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

  • Это страдает от нескольких проблем:

    mv name ../all_files/${directory}.txt
    

    Самое главное, это будет перемещать файл буквально с именем name . Вместо этого вы хотите переместить файл, на который ссылается переменная name . Для этого используйте "$name" а не name . Обратите внимание на использование двойных кавычек здесь. Это защищает имя от разбиения слов, так что это будет работать, даже если имя файла содержит пробелы или другие сложные символы.

    Аналогично, место назначения должно иметь двойные кавычки.

1

Я немного подумал и нашел два решения. Первый использует basename и dirname:

for name in */*/file
    do cp $name ../all_files/$(basename -- "$(dirname -- "$name")")
done

Этот использует разделение строк (как я изначально хотел сделать)

for name in */*/choices
    do parts=(${name//\// })
    directory=${parts[${#parts[@]} - 2]}
    cp $name all_files/${directory}.txt
done

Надеюсь, это поможет кому-то!

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