Разбейте проблему на более мелкие части. Одна из причин, по которой вы застряли, заключается в том, что вы пытаетесь создать все решение одним ударом, даже когда вы пытаетесь научиться работать с инструментами, которые используете для создания самого решения.
Вот совет, который, я надеюсь, поможет вам зажечь лампочку и поможет вам и другим начинающим программистам, когда вам придется разбирать и анализировать подобные проблемы в будущем:
Начните с точного определения того, что необходимо сделать с каждым файлом. На самом деле вы должны быть в состоянии вручную написать команды, необходимые для обработки одного конкретного имени файла, выбранного из вашего списка файлов. Не делайте работу, просто пишите команды. В вашем примере нужно переместить каждый файл, да? Поэтому для каждого файла требуется одна команда mv
. Вместо того, чтобы ломать голову над тем, как выполнить команду mv
, просто беспокойтесь о том, как ее создать. Как бы вы вручную написали только одну такую mv
команду для перемещения файла? Тогда возникает вопрос, как получитьawk
(или любой другой инструмент, который вы хотите использовать )для вывода этой команды:
mv (filename) (to-where-you-want-it)
для каждого имени файла, которое вы ему даете. Когда вы изучаете новые инструменты, гораздо проще отлаживать сценарий, который просто создает серию команд оболочки в качестве вывода, фактически ничего не делая , чем отлаживать сценарий, который просто пошел вбок и переместил сотни неправильных файлов в сотни неправильных каталогов, и теперь вы больше не уверены, где что находится.
Для начала посетите страницу man
для поиска инструмента, который, по вашему мнению, вам подойдет. Затем поэкспериментируйте с этой командой в ручном режиме,просто чтобы узнать, что вам нужно сделать, чтобы этот инструмент анализировал ваш ввод так, как вы хотите, и создавал вывод, который вам нужен. Прежде чем вы сможете написать сценарий для перемещения 100 или 1000 файлов, вам нужен сценарий, который может правильно перемещать только один файл. Так что создайте тестовый пример одного и найдите время, которое вам нужно, чтобы «подружиться» с инструментом или инструментами, которые, по вашему мнению, будут работать. Ваш пост помечен awk , и я думаю, что это мудрый выбор, так что давайте так.
awk
имеет параметр -F
, который можно использовать для указания разделителя, который awk
должен использовать для разбиения строки на составные поля. Этот разделитель может быть простым символом или любым из нескольких символов, заключенных в квадратные скобки. На языке регулярных выражений это известно как класс символов . Ваш ввод использует как дефис '-'
, так и точку '.'
в качестве разделителей полей, поэтому мы можем указать класс символов [-.]
, чтобы сообщить awk
о разделении либо на дефис, либо на точку. Внимательно заметьте, что awk
не важно, какой из них какой, и убедитесь, что ваши исходные каталоги не содержат никаких дефисов или точек.
awk
для разбиения каждого имени файла на составные поля Возьмите пример с именем файла A1-001.xyz
и попробуйте запустить его с помощью этой команды awk
вручную, чтобы узнать, что awk
делает с этим именем файла:
$ awk -F[-.] '{print $0 " " $1 " " $2 " " $3}' <<< 'A1-001.xyz'
Эта команда сообщает awk
, «Используя дефис и точку в качестве разделителей полей, напечатайте всю строку ввода ($0
), пробел, поле 1, пробел, поле 2, пробел и, наконец, поле 3.
Вывод:
A1-001.xyz A1 001 xyz
Надеюсь, это показало вам многое, :что $0
— это то, что вам нужно в исходном коде команды mv
, потому что это полное исходное имя файла; и это $2
— это то, что вам нужно в назначении команды mv
, потому что это числовое имя каталога, которое вам нужно. Самое большое осознание заключается в том, что awk
может полностью отформатировать команду mv
для вас и распечатать ее.Все, что для этого требуется, — это немного изменить оператор awk
print
. Вместо того, чтобы пытаться заставить ваш скрипт делать все, просто пусть скрипт создаст команды, которые вам нужно выполнить. Таким образом, ошибка в ваших сценариях не приведет к его взрыву и перемещению файлов в неправильные места. Он просто напечатает какой-то неправильный вывод, и вы заметите, что это неправильно, но вреда не будет.
awk
Перед именем файла может быть исходный путь. Но убедитесь, что в пути нет символов .
или -
! Таким образом, команда mv
для каждого файла, очевидно, начинается с mv
и пробела, затем имя файла (, включая полный исходный путь, возможно, ), еще один пробел и каталог, в который вы перемещаете файл. На всякий случай мы поместим косую черту после каталога назначения. Поскольку вы не меняете имя файла, мы просто укажем каталог назначения и опустим имя файла назначения. Делать это также проще, что стоит отметить. Не усложняйте задачу больше, чем это необходимо.
$ awk -F[-.] '{print "mv " $0 " " $2 "/"}' <<< '/path/to/directory1/A1-001.xyz'
mv /path/to/directory1/A1-001.xyz 001/
Посмотрите на команду print
, :начинается с mv
пробела, затем $0
— полное имя файла; другой пробел, затем $2
, который является выходным подкаталогом -. Опять же, вы должны убедиться, что ваши исходные пути НЕ содержат каких-либо дефисов или точек, потому что они имеют особое значение как разделители полей в ваших именах файлов. Проблема еще в том, что awk
не разделит ваши поля должным образом, и ваш скрипт сломается.
Но целевой каталог — это не просто $2
, перед ним стоит префикс, как и перед именем исходного файла. Мы можем заставить awk
распечатать это для нас, так как это каждый раз одно и то же :
.
$ awk -F[-.] '{print "mv " $0 " /path/to/directory2/" $2 "/"}' <<< '/path/to/directory1/A1-001.xyz'
mv /path/to/directory1/A1-001.xyz /path/to/directory2/001/
Выглядит многообещающе.Теперь составьте список файлов вfile-list.txt
:
$ cat file-list.txt
A1-001.xyz
A29-002.xyz
A82-003.xyz
, а затем выполните команду awk
для всего этого списка файлов. Помните, здесь нет ничего плохого, потому что все, что awk
делает, это печатает материал. На самом деле он ничего не делает для перемещения файлов. Он просто показывает вам команды, которые будут делать то, что вы хотите.
$ awk -F[-.] '{print "mv " $0 " /path/to/directory2/" $2 "/"}' < file-list.txt
mv A1-001.xyz /path/to/directory2/001/
mv A29-002.xyz /path/to/directory2/002/
mv A82-003.xyz /path/to/directory2/003/
Если вам нужно переместить большое количество файлов, вы можете передать приведенную выше команду awk
в less
, чтобы вы могли внимательно ее изучить. Ищите точки и тире в неправильных местах или другие странные символы в именах файлов или каталогов. Если вы хотите, вы можете скопировать -и -и вставить образец строки этого вывода в приглашение оболочки, чтобы убедиться, что он работает правильно. Но это достаточно простой пример, который мы можем проверить путем проверки. Как только вы убедитесь, что этот список команд mv
— это то, что вы хотите сделать, просто направьте вывод awk
прямо в sh
для его выполнения. Если вы хотите видеть команды во время их выполнения, используйте sh -v
вместо простоsh
:
$ awk -F[-.] '{print "mv " $0 " /path/to/directory2/" $2 "/"}' < file-list.txt | sh -v
mv A1-001.xyz /path/to/directory2/001/
mv A29-002.xyz /path/to/directory2/002/
mv A82-003.xyz /path/to/directory2/003/
$
Надеюсь, вы не возражаете против такой подробной разбивки, но такого рода вопросы часто возникают на Stack Exchange, и многие начинающие скриптеры считают, что их проблема — уникальная, -нестандартная проблема, которая требует уникальное решение.
Настоящим ключом к написанию сценариев является осознание того, что сценарии предоставляют универсальные инструменты, которые могут решать самые разные задачи, и одним из первых шагов к получению навыков является изучение того, как делать небольшие вещи с помощью этих инструментов, а затем комбинировать их. маленькие вещи в большие и большие вещи.
Первый шаг состоял в том, чтобы просто научиться говорить awk
как разбить имя файла так, как нам нужно. Это критический шаг каждый раз, когда вы пытаетесь проанализировать поля компонентов из имени файла, в который встроено несколько фрагментов информации.
Вторым шагом было указание awk автоматически печатать части команды, которые всегда были одинаковыми для каждого файла (mv
в начале, путь назначения перед полем $2
), и разместить извлеченные поля имени файла в правильных местах. Операторы print
и им подобные являются одной из самых основных частей любого типа кодирования, и я не могу припомнить большого вреда, который когда-либо причинялся оператором well -place print
. Чтобы быть уверенным, вы хотите выводить только то, что необходимо, но когда вы учитесь и не знаете, что такое переменная, напечатайте ее, спросить редко бывает больно. В долгосрочной перспективе вы вернете этот оператор печати обратно, но весь смысл техники написания сценариев «напечатать -это -, затем -передать -в -оболочку» заключается в том, что у вас есть " пробный прогон», потому что вы всегда просматриваете команды оболочки, выводимые вашим сценарием, прежде чем вы на самом деле передаете их оболочке для выполнения. В сложных случаях даже добавление комментариев в ваш вывод является честной игрой, чтобы «показать вашу работу» :
.
$ awk -F[-.] '{print "# move file " $0 " to subdir " $2; print "mv " $0 " /path/to/directory2/" $2 "/"}' < file-list.txt
# move file A1-001.xyz to subdir 001
mv A1-001.xyz /path/to/directory2/001/
# move file A29-002.xyz to subdir 002
mv A29-002.xyz /path/to/directory2/002/
# move file A82-003.xyz to subdir 003
mv A82-003.xyz /path/to/directory2/003/
И третий ключ, возможно, тесно связанный с моим вторым пунктом, но который, как мне кажется, часто упускается из виду: когда вы делаете что-то, что требует от вас некоторого напряжения, не пишите сценарий, который потенциально может пойти ошибаетесь и оставляете свои файлы разбросанными по множеству разных, но неправильных мест. Просто напишите скрипт, который пишет скрипт для выполнения работы. Так гораздо проще устранять неполадки. Затем, когда у вас, наконец, будет правильный сценарий, просто передайте вывод сценария (в вашем примере, серию команд mv
, по одной на файл )в оболочку, и они будут запущены.