#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'
При этом выполняются следующие операции над каждой строкой в следующем порядке,
p
: распечатать текущую строку как есть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@ @'
При этом выполняются следующие операции над каждой строкой в следующем порядке,
N
: Соединить текущую строку ввода (строку в пространстве шаблонов) со следующей строкой ввода с символом новой строки между ними.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'