Я пытаюсь скопировать кучу файлов под каталогом, и у нескольких файлов есть пробелы и одиночные кавычки в их именах. Когда я пытаюсь соединить find
и grep
с xargs
, я получаю следующую ошибку:
find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote
Любые предложения по более надежному использованию xargs?
Это находится на MacOS 10.5.3 с BSD xargs
.
Вы также можете объединить все это в одну команду find:
find . -iname "*foobar*" -exec cp "{}" ~/foo/bar \;
Это будет обрабатывать имена файлов и каталоги с пробелами в них. Вы можете использовать -name для получения результатов, чувствительных к регистру.
(Эти аргументы командной строки будут работать с GNU find; я не знаю, доступны ли они с помощью BSD или OS X.)
find . -print0 | grep --null 'FooBar' | xargs -0 ...
Я не знаю, поддерживает ли grep
--null
, и не поддерживает ли xargs
-0
на Leopard, но на GNU все это хорошо.
Это более эффективно, так как он не запускает "cp" несколько раз:
find -name '*FooBar*' -print0 | xargs -0 cp -t ~/foo/bar
Я столкнулся с той же проблемой. Вот как я это решил:
find . -name '*FoooBar*' | sed 's/.*/"&"/' | xargs cp ~/foo/bar
Я использовал sed
, чтобы подставить каждую строку ввода той же строкой, но окружен двойными кавычками. На странице sed
"... Амперсанд (` `& ''), появляющийся в замене, заменяется строкой, соответствующей RE..." - в этом случае .*
, весь линия.
Это решает ошибку xargs: unterminated quote
.
sed s/.*/\"&\"/
чтобы заставить его работать.
Самый простой способ сделать то, что хочет исходный плакат, - это изменить разделитель от любого пробела до только конца строки следующим образом:
find whatever ... | xargs -d "\n" cp -t /var/tmp
sed -e 's_\(.*\)_"\1"_g'
чтобы sed -e 's_\(.*\)_"\1"_g'
в кавычки имя файла
Этот метод работает на Mac OSx Lion 10.7.5
find . | grep FooBar | xargs -I{} cp {} ~/foo/bar
Изменить: также просто протестирован точный синтаксис, который вы опубликовали. Это также отлично работало на 10.7.5.
Вот портативное (POSIX) решение, то есть одно, которое не требует find
, xargs
или cp
конкретных расширений GNU:
find . -name "*FooBar*" -exec sh -c 'cp -- "$@" ~/foo/bar' sh {} +
Он будет корректно обрабатывать файлы и каталоги со встроенными пространствами, новыми символами и т.д., и более эффективен (читается быстрее), чем принятый ответ.
find
может делать то, что делает xargs
без каких-либо накладных расходов.
Посмотрите на использование опции командной строки -null для xargs с опцией -print0 в поиске.
Для тех, кто полагается на команды, кроме поиска, например ls
:
find . | grep "FooBar" | tr \\n \\0 | xargs -0 -I{} cp "{}" ~/foo/bar
-I
подразумевает -L 1
find | perl -lne 'print quotemeta' | xargs ls -d
Я считаю, что это будет надежно работать для любого персонажа, кроме строки (и я подозреваю, что если у вас есть фиды в именах файлов, то у вас проблемы с этим хуже). Он не требует GNU findutils, просто Perl, поэтому он должен работать практически везде.
mkdir test && cd test && perl -e 'open $fh, ">", "this-file-contains-a-\n-here"' && ls | od -tx1
Я обнаружил, что следующий синтаксис хорошо работает для меня.
find /usr/pcapps/ -mount -type f -size +1000000c | perl -lpe ' s{ }{\\ }g ' | xargs ls -l | sort +4nr | head -200
В этом примере я ищу самые большие 200 файлов более 1 000 000 байт в файловой системе, установленной на "/usr/pcapps".
Линейный лайнер Perl между "find" и "xargs" экранирует/кавыкает каждый пробел, поэтому "xargs" передает любое имя файла со встроенными записями в "ls" как один аргумент.
Билл Старр Пт, 23 янв 2009, 17:40 EST
Помните, что большинство вариантов, обсуждаемых в других ответах, не являются стандартными для платформ, которые не используют утилиты GNU (например, Solaris, AIX, HP-UX). См. Спецификацию POSIX для стандартного поведения xargs.
Я также обнаружил поведение xargs, благодаря которому он запускает команду хотя бы один раз, даже без ввода, в качестве неприятности.
Я написал свою собственную приватную версию xargs (xargl) для решения проблем пространств в именах (только новые линии разделены - хотя сочетание "find... -print0" и "xargs -0" довольно аккуратно, учитывая, что имена файлов не могут содержать символы ASCII NUL '\ 0'. Мой xargl не такой полный, как его нужно было бы опубликовать, особенно потому, что у GNU есть объекты, которые по крайней мере хороши.
Для меня я пытался сделать что-то совсем другое. Я хотел скопировать файлы txt в папку tmp. Имена файлов txt содержат пробелы и символы апострофа. Это сработало на моем mac.
$ find . -type f -name '*.txt' | sed 's/'"'"'/\'"'"'/g' | sed 's/.*/"&"/' | xargs -I{} cp -v {} ./tmp/
Я немного поиграл с этим, начал размышлять о модификации xargs и понял, что для такого варианта использования, о котором мы говорим здесь, простое повторное выполнение в python - лучшая идея. Во-первых, наличие ~ 80 строк кода для всего, что означает, что легко понять, что происходит, и если требуется другое поведение, вы можете просто взломать его в новый script за меньшее время, чем требуется чтобы получить ответ где-то вроде stackoverflow.
См. https://github.com/johnallsup/jda-misc-scripts/blob/master/yargs и https://github.com/johnallsup/jda-misc-scripts/blob/master/zargs.py
С помощью yargs, записанного (и установленного python3), вы можете ввести
find .|grep "FooBar"|yargs -l 203 cp --after ~/foo/bar
выполнить копирование 203 файлов за раз. (Здесь 203 - это просто местозаполнитель, конечно, и использование странного числа, такого как 203, дает понять, что это число не имеет другого значения.)
Если вы действительно хотите что-то быстрее и без необходимости использовать python, возьмите zargs и yargs в качестве прототипов и перепишите на С++ или C.
С помощью bash (не POSIX) вы можете использовать подстановку процессов, чтобы получить текущую строку внутри переменной. Это позволяет использовать кавычки для вызова специальных символов:
while read line ; do cp "$line" ~/bar ; done < <(find . | grep foo)
Я использовал ответ Bill Star, слегка измененный в Solaris:
find . -mtime +2 | perl -pe 's{^}{\"};s{$}{\"}' > ~/output.file
это будет помещать кавычки вокруг каждой строки. Я не использовал параметр "-l", хотя он, вероятно, помог бы.
Список файлов, в которые я шел, может иметь '-', но не новые строки. Я не использовал выходной файл с любыми другими командами, так как хочу просмотреть, что было найдено до того, как я просто начал массово удалять их через xargs.
Версия perl, приведенная выше, не будет работать хорошо для встроенных новых строк (только с пробелами). Для тех, напр. solaris, где у вас нет инструментов gnu, более полная версия может быть (с использованием sed)...
find -type f | sed 's/./\\&/g' | xargs grep string_to_find
скорректируйте аргументы find и grep или другие команды по мере необходимости, но sed установит ваши встроенные новые строки/пробелы/вкладки.
Если версии find и xarg в вашей системе не поддерживают переключатели -print0
и -0
(например, AIX find и xargs), вы можете использовать этот ужасно выглядящий код:
find . -name "*foo*" | sed -e "s/'/\\\'/g" -e 's/"/\\"/g' -e 's/ /\\ /g' | xargs cp /your/dest
Здесь sed позаботится о том, чтобы избежать пробелов и цитат для xargs.
Протестировано на AIX 5.3
Вам может понадобиться grep каталог Foobar, например:
find . -name "file.ext"| grep "FooBar" | xargs -i cp -p "{}" .
-i
устарела, и вместо -I
следует использовать -I
.
Для пользователей глупой версии, отличной от GNU, решение Bill Starr не работает, если в имени файла есть апострофы. У rjb1 также есть такая же проблема, я думаю, хотя я не могу повторить ее с помощью теста. Работа Карла Ямамото-Фурста работает.
Если вы используете bash, вы можете преобразовать stdout в массив строк mapfile
,
find . | grep "FooBar" | (mapfile -t; cp "${MAPFILE[@]}" ~/foobar)
преимущества:
Вы можете добавить другие аргументы в имена файлов. Для cp
вы также можете:
find . -name '*FooBar*' -exec cp -t ~/foobar -- {} +
однако некоторые команды не имеют такой функции.
недостатки:
Ну... кто знает, доступен ли bash в OS X?
|grep
не будет работать
--delimiter
(-d
). Попробуйте это с\n
в качестве разделителя. Это неxargs
разделить строки с пробелами на несколько слов / аргументов.