Как я могу использовать xargs для копирования файлов с пробелами и кавычками в своих именах?
Я пытаюсь скопировать кучу файлов в каталог, и в некоторых файлах есть пробелы и одинарные кавычки. Когда я пытаюсь xargs
вместе find
и grep
с xargs
, я получаю следующую ошибку:
find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote
Какие-нибудь предложения для более надежного использования xargs?
Это на Mac OS X 10.5.3 (Leopard) с BSD xargs
.
Ответы
Ответ 1
Вы можете объединить все это в одну команду find
:
find . -iname "*foobar*" -exec cp -- "{}" ~/foo/bar \;
Это будет обрабатывать имена файлов и каталогов с пробелами в них. Вы можете использовать -name
для получения результатов с учетом регистра.
Примечание. Флаг --
передаваемый в cp
позволяет ему обрабатывать файлы, начиная с опции -
as.
Ответ 2
find . -print0 | grep --null 'FooBar' | xargs -0 ...
Я не знаю, поддерживает ли grep
--null
, и не поддерживает ли xargs
-0
на Leopard, но на GNU все это хорошо.
Ответ 3
Самый простой способ сделать то, что хочет исходный плакат, - это изменить разделитель от любого пробела до только конца строки следующим образом:
find whatever ... | xargs -d "\n" cp -t /var/tmp
Ответ 4
Это более эффективно, так как он не запускает "cp" несколько раз:
find -name '*FooBar*' -print0 | xargs -0 cp -t ~/foo/bar
Ответ 5
Я столкнулся с той же проблемой. Вот как я это решил:
find . -name '*FoooBar*' | sed 's/.*/"&"/' | xargs cp ~/foo/bar
Я использовал sed
, чтобы подставить каждую строку ввода той же строкой, но окружен двойными кавычками. На странице sed
"... Амперсанд (` `& ''), появляющийся в замене, заменяется строкой, соответствующей RE..." - в этом случае .*
, весь линия.
Это решает ошибку xargs: unterminated quote
.
Ответ 6
Этот метод работает на Mac OS X v10.7.5 (Lion):
find . | grep FooBar | xargs -I{} cp {} ~/foo/bar
Я также проверил точный синтаксис, который вы разместили. Это также хорошо работало на 10.7.5.
Ответ 7
Только не используйте xargs
. Это аккуратная программа, но она не подходит для find
когда сталкиваешься с нетривиальными случаями.
Вот переносимое (POSIX) решение, то есть решение, которое не требует find
, xargs
или cp
GNU:
find . -name "*FooBar*" -exec sh -c 'cp -- "[email protected]" ~/foo/bar' sh {} +
Обратите внимание на окончание +
вместо более обычного ;
,
Это решение:
-
правильно обрабатывает файлы и каталоги со встроенными пробелами, символами новой строки или любыми экзотическими символами.
-
работает в любой системе Unix и Linux, даже в тех, которые не предоставляют инструментарий GNU.
-
не использует xargs
которая является хорошей и полезной программой, но требует слишком много настроек и нестандартных функций для правильной обработки результатов find
.
-
также более эффективен (читай быстрее), чем принятый, и большинство, если не все остальные ответы.
Также обратите внимание, что, несмотря на то, что указано в некоторых других ответах или комментариях, цитирование {}
бесполезно (если только вы не используете раковину экзотических fish
).
Ответ 8
Посмотрите на использование опции командной строки -null для xargs с опцией -print0 в поиске.
Ответ 9
Для тех, кто полагается на команды, кроме поиска, например ls
:
find . | grep "FooBar" | tr \\n \\0 | xargs -0 -I{} cp "{}" ~/foo/bar
Ответ 10
Я обнаружил, что следующий синтаксис работает хорошо для меня.
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" в качестве единственного аргумента.
Ответ 11
find | perl -lne 'print quotemeta' | xargs ls -d
Я считаю, что это будет надежно работать для любого персонажа, кроме строки (и я подозреваю, что если у вас есть фиды в именах файлов, то у вас проблемы с этим хуже). Он не требует GNU findutils, просто Perl, поэтому он должен работать практически везде.
Ответ 12
Помните, что большинство вариантов, обсуждаемых в других ответах, не являются стандартными для платформ, которые не используют утилиты GNU (например, Solaris, AIX, HP-UX). См. Спецификацию POSIX для стандартного поведения xargs.
Я также обнаружил поведение xargs, благодаря которому он запускает команду хотя бы один раз, даже без ввода, в качестве неприятности.
Я написал свою собственную приватную версию xargs (xargl) для решения проблем пространств в именах (только новые линии разделены - хотя сочетание "find... -print0" и "xargs -0" довольно аккуратно, учитывая, что имена файлов не могут содержать символы ASCII NUL '\ 0'. Мой xargl не такой полный, как его нужно было бы опубликовать, особенно потому, что у GNU есть объекты, которые по крайней мере хороши.
Ответ 13
С Bash (не POSIX) вы можете использовать подстановку процесса, чтобы получить текущую строку внутри переменной. Это позволяет вам использовать кавычки для экранирования специальных символов:
while read line ; do cp "$line" ~/bar ; done < <(find . | grep foo)
Ответ 14
Я пытался сделать что-то немного другое. Я хотел скопировать мои файлы .txt в мою папку TMP. Имена файлов .txt содержат пробелы и символы апострофа. Это сработало на моем Mac.
$ find . -type f -name '*.txt' | sed 's/'"'"'/\'"'"'/g' | sed 's/.*/"&"/' | xargs -I{} cp -v {} ./tmp/
Ответ 15
bill_starr Perl-версия не будет хорошо работать для встроенных символов новой строки (справляется только с пробелами). Для тех, например, в Solaris, где у вас нет инструментов GNU, может быть более полная версия (с использованием sed)...
find -type f | sed 's/./\\&/g' | xargs grep string_to_find
настройте аргументы find и grep или другие команды, как вам требуется, но sed исправит ваши встроенные символы новой строки/пробелов/табуляции.
Ответ 16
Я использовал ответ Билла Стар, слегка измененный на Solaris:
find . -mtime +2 | perl -pe 's{^}{\"};s{$}{\"}' > ~/output.file
Это поместит кавычки вокруг каждой строки. Я не использовал опцию "-l", хотя это, вероятно, помогло бы.
Список файлов, который я собирался, хотя мог иметь "-", но не переводы строк. Я не использовал выходной файл с какими-либо другими командами, так как хочу просмотреть то, что было найдено, прежде чем я просто начну массово удалять их через xargs.
Ответ 17
Если версии 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
Ответ 18
Я немного поиграл с этим, начал размышлять над модификацией xargs и понял, что для случая использования, о котором мы здесь говорим, простая переопределение в Python - лучшая идея.
Во-первых, наличие ~ 80 строк кода для всего этого означает, что легко понять, что происходит, и если требуется другое поведение, вы можете просто взломать его в новый скрипт за меньшее время, чем требуется, чтобы получить ответ на что-то вроде.
См. Https://github.com/johnallsup/jda-misc-scripts/blob/master/yargs и https://github.com/johnallsup/jda-misc-scripts/blob/master/zargs.py.
С написанным yargs (и установленным Python 3) вы можете набрать:
find .|grep "FooBar"|yargs -l 203 cp --after ~/foo/bar
сделать копирование 203 файлов одновременно. (Здесь 203, конечно, просто заполнитель, и использование странного числа, такого как 203, дает понять, что это число не имеет другого значения.)
Если вам действительно нужно что-то более быстрое и без Python, возьмите zargs и yargs в качестве прототипов и перепишите в C++ или C.
Ответ 19
Я создал небольшой переносимый скрипт-обертку под названием "xargsL" вокруг "xargs", который решает большинство проблем.
В отличие от xargs, xargsL принимает одно имя пути на строку. Имена путей могут содержать любой символ, кроме (очевидно) символа новой строки или байтов NUL.
В списке файлов цитирование не допускается и не поддерживается - ваши имена файлов могут содержать все виды пробелов, обратных косых черт, обратных черт, подстановочных знаков оболочки и т.п. - xargsL будет обрабатывать их как буквенные символы, никакого вреда.
В качестве дополнительной бонусной функции xargsL не будет запускать команду один раз, если нет ввода!
Обратите внимание на разницу:
$ true | xargs echo no data
no data
$ true | xargsL echo no data # No output
Все аргументы, переданные xargsL, будут переданы xargs.
Вот сценарий оболочки POSIX "xargsL":
#! /bin/sh
# Line-based version of "xargs" (one pathname per line which may contain any
# amount of whitespace except for newlines) with the added bonus feature that
# it will not execute the command if the input file is empty.
#
# Version 2018.76.3
#
# Copyright (c) 2018 Guenther Brunthaler. All rights reserved.
#
# This script is free software.
# Distribution is permitted under the terms of the GPLv3.
set -e
trap 'test $? = 0 || echo "$0 failed!" >& 2' 0
if IFS= read -r first
then
{
printf '%s\n' "$first"
cat
} | sed 's/./\\&/g' | xargs ${1+"[email protected]"}
fi
Поместите скрипт в какой-то каталог в вашем $ PATH и не забудьте
$ chmod +x xargsL
скрипт, чтобы сделать его исполняемым.
Ответ 20
Для пользователей глупой версии, отличной от GNU, решение Bill Starr не работает, если в имени файла есть апострофы. У rjb1 также есть такая же проблема, я думаю, хотя я не могу повторить ее с помощью теста. Работа Карла Ямамото-Фурста работает.
Ответ 21
Вам может понадобиться grep каталог Foobar, например:
find . -name "file.ext"| grep "FooBar" | xargs -i cp -p "{}" .
Ответ 22
Задача фрейма - вы спрашиваете, как использовать xargs. Ответ таков: вы не используете xargs, потому что он вам не нужен.
Комментарий user80168
описывает способ сделать это напрямую с помощью cp без вызова cp для каждого файла:
find . -name '*FooBar*' -exec cp -t /tmp -- {} +
Это работает, потому что:
- флаг
cp -t
позволяет cp -t
целевой каталог ближе к началу cp
, а не к концу. От man cp
:
-t, --target-directory=DIRECTORY
copy all SOURCE arguments into DIRECTORY
-
Флаг --
указывает cp
интерпретировать все после как имя файла, а не как флаг, поэтому файлы, начинающиеся с -
или --
не путают cp
; вам все еще нужно это, потому что символы -
/--
интерпретируются cp
, тогда как любые другие специальные символы интерпретируются оболочкой.
-
Команда find -exec command {} +
option по сути делает то же самое, что и xargs. От man find
:
-exec command {} +
This variant of the -exec action runs the specified command on
the selected files, but the command line is built by appending
each selected file name at the end; the total number of invoca‐
matched files. The command line is built in much the same way
that xargs builds its command lines. Only one instance of '{}'
is allowed within the command, and (when find is being invoked
from a shell) it should be quoted (for example, '{}') to protect
it from interpretation by shells. The command is executed in
the starting directory. If any invocation returns a non-zero
value as exit status, then find returns a non-zero exit status.
If find encounters an error, this can sometimes cause an immedi‐
ate exit, so some pending commands may not be run at all. This
variant of -exec always returns true.
Используя это непосредственно в find, вы избегаете необходимости вызова канала или оболочки, так что вам не нужно беспокоиться о каких-либо неприятных символах в именах файлов.
Ответ 23
Если вы используете Bash, вы можете преобразовать стандартный вывод в массив строк с помощью mapfile
:
find . | grep "FooBar" | (mapfile -t; cp "${MAPFILE[@]}" ~/foobar)
Преимущества:
- Он встроенный, поэтому он быстрее.
- Выполните команду со всеми именами файлов за один раз, чтобы она работала быстрее.
-
Вы можете добавить другие аргументы к именам файлов. Для cp
вы также можете:
find . -name '*FooBar*' -exec cp -t ~/foobar -- {} +
однако некоторые команды не имеют такой функции.
Недостатки:
- Возможно, не очень хорошо масштабируется, если файлов слишком много. (Предел? Я не знаю, но я протестировал файл списка 10 МБ, который включает в себя имена файлов 10000+ без проблем, под Debian)
Ну... кто знает, доступен ли Bash на OS X?