Intereting Posts
Резервное копирование полной установки Linux Синтаксис для команды rm. Обеспокоенный использованием опции -r как выглядит запись «удаленный файл» в журнале В чем смысл `shared` memory в команде` free` shell? Red Hat Enterprise Linux 6.5 перезагружает корневую файловую систему только для чтения после спящего режима Unity freeze randomly Как исключить каталоги с пробелами через PRUNEPATHS локатора? Как использовать `lpr` для печати нескольких файлов с использованием 4 страниц на листе? Действительно ли режим Powersave на процессоре помогает экономить электроэнергию? Что происходит при выполнении этой команды cd / | VI? Как разрешить пользователям без полномочий root управлять службой systemd с помощью экземпляров? Systemd и процесс нереста Что такое отсутствие разрешения группы в новых каталогах? Не удается загрузить Debian Two Blu-Ray Disk Выполнить команду при запуске системы для любых пользователей

Создание каталогов в цикле и перемещение файлов в эти каталоги

Рассмотрим каталог с N файлами.

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

ls > list 

Затем я хочу создать n подкаталогов в том же каталоге, что можно сделать с помощью

 mkdir direc{1..n} 

Теперь я хочу перенести первый m или сказать первые 5 (1-5) файлов из list в direc1 , следующие 5 файлов, т.е. 6-10 в direc2 и т. Д.

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

 list=(*) # an array containing all the current file and subdir names nd=5 # number of new directories to create ((nf = (${#list[@]} / nd) + 1)) # number of files to move to each dir # add 1 to deal with shell's integer arithmetic # brace expansion doesn't work with variables due to order of expansions echo mkdir $(printf "direc%d " $(seq $nd)) # move the files in chunks # this is an exercise in arithmetic to get the right indices into the array for (( i=1; i<=nd; i++ )); do echo mv "${list[@]:(i-1)*nf:nf}" "direc$i" done 

удалите две команды «эхо» после того, как вы протестировали это.

Или, если вы хотите пойти с фиксированным количеством файлов в каталоге, это проще:

 list=(*) nf=10 for ((d=1, i=0; i < ${#list[@]}; d++, i+=nf)); do echo mkdir "direc$d" echo mv "${list[@]:i:nf}" "direc$d" done 
 #!/bin/sh d=1 # index of the directory f=0 # number of files already copied into direc$d for x in *; do # iterate over the files in the current directory # Create the target directory if this is the first file to be copied there if [ $f -eq 0 ]; then mkdir "direc$d"; fi # Move the file mv -- "$x" "direc$d" f=$((f+1)) # If we've moved 5 files, go on to the next directory if [ $f -eq 5 ]; then f=0 d=$((d+1)) fi done 

Полезные ссылки:

  • Арифметическое разложение $((…))
  • Условные выражения [ … ]
  • Почему мой сценарий оболочки задыхается от пробелов или других специальных символов? почему цитаты и -- в mv -- "$x" …
 d=0; set ./somedirname #init dir counter; name for f in ./* #glob current dir do [ -f "$f" ] && set "$f" "$@" && #if "$f" is file and... [ "$d" -lt "$((d+=$#>5))" ] || continue #d<(d+($#>5)) or continue mkdir "$6$d" && mv "$@$d" || ! break #mk tgtdir and mv 5 files or shift 5 #break with error done 

Вышеприведенная команда использует возможности массива arg-массива оболочки для конкатенации строк в голову и хвост. Например, если вы написали функцию:

 fn(){ printf '<%s>\n' "42$@"; } 

… и назвал это так:

 fn show me 

… он будет печатать:

 <42show> <me> 

… потому что вы можете добавлять или добавлять к первому или последнему элементу (соответственно) в массиве arg, просто закрывая свои кавычки вокруг вашей предварительно / прикрепленной строки.

Аргумент arg также выполняет двойную функцию, так как он также служит в качестве счетчика. Параметр $# shell всегда позволяет нам точно знать, сколько элементов у нас было уложено до сих пор.

Но … Вот шаг за шагом:

  1. d=0; set ./somedirname
    • $d var будет увеличиваться на единицу для каждого созданного нового каталога. Здесь он инициализируется нулем.
    • ./somedirname – это то, что вам нравится. Префикс ./ важно, однако, – это не только то, что он полностью управляет всеми операциями в текущем каталоге, но также позволяет указать любое имя, которое вы хотели бы (если вы хотите сходить с ума и использовать новые строки или начать с дефисами может безопасно – но это, вероятно, нецелесообразно) . Поскольку argname всегда будет начинаться с ./ no, команда никогда не будет неверно истолковывать его как параметр в командной строке.
  2. for f in ./*
    • Это запустит цикл по всем (если есть) совпадениям для * в текущем каталоге. Опять же, каждый матч имеет префикс ./ .
  3. [ -f "$f" ]
    • проверяет, что соответствие каждой итерации определенно является обычным файлом (или ссылкой на один) и …
  4. set "$f" "$@"
    • складывает совпадения друг против друга в массиве оболочки. Таким образом. ./somedirname всегда находится в хвосте массива.
  5. [ "$d" -lt "$((d+=$#>5))" ]
    • добавляет 1 к $d если в "$@" содержится более 5 элементов массива, одновременно проверяя результат для приращения.
  6. || continue
    • Если какой-либо из [ -f "$f" ] set ... [ "$d" -lt... команды не возвращают true, цикл продолжается до следующей итерации и не пытается завершить оставшуюся часть цикла , Это эффективно и безопасно.
  7. mkdir "$6$d"
    • Поскольку предложение continue гарантирует, что мы можем сделать это только до этой точки, если $# > 5 наше ./somedirname теперь находится в $6 а значение $d просто увеличилось на единицу. Таким образом, для первой группы из 5 файлов, сопоставленных и перемещенных, создается каталог с именем ./somedirname1 и для пятого ./somedirname5 и т. Д. Важно отметить, что эта команда терпит неудачу, если какой-либо путь с указанным именем пути уже существует. Другими словами, эта команда будет успешной только в том случае, если уже нет каталога с тем же именем, который уже существует.
  8. mv "$@$d"

    • Это расширяет массив при привязке значения для $d к хвосту последнего элемента, который является именем целевого каталога. Таким образом, он расширяется следующим образом:

    mv ./file5 ./file4 ./file3 ./file2 ./file1 ./somedirname1

    • … это именно то, что вы хотите.
  9. || ! break

    • Если какая-либо из двух предыдущих команд не завершилась успешно по какой-либо причине, break for цикла s. ! отправляет логический обратный возврат break который обычно равен нулю, поэтому break возвращает 1. Таким образом, цикл вернет false, если какая-либо ошибка произойдет в любой из предыдущих команд. Это важно – for циклов – в отличие от циклов while/until циклов – не подразумевают тесты, только итерации. Без явного тестирования возврата этих двух команд оболочка не обязательно останавливается на ошибке – и set -e скорее всего полностью уничтожит родительскую оболочку. Скорее это обеспечивает значимый возврат и что цикл не будет продолжать итерации, если что-то пойдет не так.

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

  10. shift 5
    • Этот shift сбрасывает первые 5 элементов в массиве arg оболочки – который помещает ./somedirname обратно в $1 и сбрасывает состояние массива для следующей итерации.

С помощью этой awk-программы вы можете создавать команды оболочки и, в случае сомнений, заранее проверять, являются ли они правильными …

 awk -vn=5 '{ printf "mv \"%s\" %s\n", $0, "direc" int((NR-1)/n)+1 }' list 

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

 ls | awk -vn=5 '{ printf "mv \"%s\" %s\n", $0, "direc" int((NR-1)/n)+1 }' | sh 

Вы можете определить другие значения, кроме 5, если вы измените настройку n = 5.

Если вы также хотите создать целевые каталоги на лету, вот вариант …

 ls | awk -vn=5 ' NR%n==1 { ++c ; dir="direc"c ; print "mkdir "dir } { printf "mv \"%s\" %s\n", $0, dir } ' | sh