Когда оболочка выполняет внешнюю команду, длина командной строки после расширения любого шаблона подстановки имени файла, такого как *
, не должна превышать определенной длины.
В вашем случае grep 'PATTERN' *
превращается в слишком длинную команду для выполнения оболочкой.
Во втором примере:
for i in $(`cat MyFiles`); do echo $i; done
вы пытаетесь перебрать имена файлов, хранящиеся в MyFiles
, но вы очень неправильно понимаете синтаксис.
$(`cat MyFiles`)
совпадает с
$( $(cat MyFiles) )
, что означает, что содержимое MyFiles
будет интерпретироваться как команда. Вот почему вы получаете ошибку command not found
.
Есть несколько способов исправить это.но перебирать содержимое вашего файла в цикле не очень хорошо.
Стивен дает хорошее решение в своем ответе , а другое решение, предполагая, что ваш текущий рабочий каталог — это папка Maildir,
find. -type f -exec grep 'PATTERN' {} +
Это выполнит grep
несколько раз для как можно больших пакетов файлов .
Это похоже на
printf '%s\n' * | xargs grep 'PATTERN'
, но команда find
обрабатывает имена файлов с пробелами и встроенными символами новой строки.
Команда printf
выводит одно имя файла в строке. У него нет той же проблемы, что и у grep 'PATTERN' *
, так как это, скорее всего, , встроенная -в команду , и поэтому ее не нужно выполнять в оболочке как внешнюю команду.
Ваше циклическое решение также будет работать, но вместо того, чтобы перебирать результат cat
, вы можете просто выполнить
for name in *; do
grep 'PATTERN' "$name"
done
Это предполагает, что в текущем каталоге есть только обычные файлы .
Чтобы убедиться, что вы обрабатываете только почтовые сообщения, вы можете использовать
for name in *,*; do
grep 'PATTERN' "$name" /dev/null
done
Перебирает имена, которые содержат хотя бы одну запятую. Я также добавил /dev/null
, чтобы заставить grep
выводить имена файлов, соответствующие заданному шаблону. Вы можете удалить /dev/null
и вместо этого использовать -H
с grep
, если ваш grep
поддерживает это.
Подобный цикл выполняется медленно, поскольку мы выполняем grep
один раз для каждого отдельного файла в каталоге.
На вашем месте я бы сделал это небольшими партиями и проверил с копией, но что-то вроде этого подойдет вам
while read n; do read f; mv $f "$n.${f##*.}"; done < file
Прежде чем начать, рекомендуется проверить вывод
while read n; do read f; echo mv $f "$n.${f##*.}"; done < file > checkfile
Чтобы убедиться, что в файле переименования нет ошибок.
Безопаснее, если у вас есть место и время для копирования, чтобы сохранить ваши оригиналы в безопасности, пока вы не будете счастливы....
while read n; do read f; cp $f "/a/safe/location/$n.${f##*.}"; done < file
Примечание
Чтобы отменить процесс, вам просто нужно изменить порядок имен в команде mv
mv "$n.${f##*.}" $f
Предполагая, что "it's name example" — это строка в строке непосредственно над текущим .mkv
именем файла, т. е. заданным
$ cat file.txt
Taken S01 E01
74857.mkv
Taken S01 E02
74858.mkv
ты действительно хочешь
74857.mkv > Taken S01 E01.mkv
74858.mkv > Taken S01 E02.mkv
, затем с GNU parallel
иmv
:
parallel -N2 echo mv -- {2} {1}.mkv < file.txt
Удалите echo
, как только убедитесь, что он составляет правильные команды.
Сperl
:
perl -lne '$dst = "$_.mkv"; $src = <>; chomp $src; rename $src, $dst
or warn "$src -> $dst: $!\n"' your-file
Сzsh
:
zmodload zsh/files # to get a builtin mv
while IFS= read -ru3 dst && IFS= read -ru3 src; do
mv -- "$src" "$dst.mkv"
done 3< your-file