Как я могу использовать xargs для копирования файлов с пробелами и кавычками в их именах?
Я пытаюсь скопировать кучу файлов под каталогом, и в некоторых файлах есть пробелы и одинарные кавычки в их именах. Когда я пытаюсь связать вместе find
и grep
С xargs
, Я получаю следующую ошибку:
find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote
любые предложения для более надежного использования xargs?
на Mac OS X 10.5.3 (Леопард) с BSD xargs
.
21 ответ:
вы можете объединить все это в единую :
find . -iname "*foobar*" -exec cp -- "{}" ~/foo/bar \;
это будет обрабатывать имена файлов и каталогов с пробелами в них. Вы можете использовать
-name
для получения результатов с учетом регистра.Примечание:
--
флаг переданcp
предотвращает его от обработки файлов, начиная с-
как варианты.
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 whatever ... | xargs -d "\n" cp -t /var/tmp
я столкнулся с такой же проблемой. Вот как я это решил:
find . -name '*FoooBar*' | sed 's/.*/"&"/' | xargs cp ~/foo/bar
Я
sed
чтобы заменить каждую строку ввода той же строкой, но в окружении двойных кавычек. Изsed
man page,"...амперсанд ( " & " ), появляющийся в замене, заменяется строкой, соответствующей RE..." -- в данном случае,.*
вся линия.скачать
xargs: unterminated quote
ошибка.
этот метод работает на Mac OS X v10.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 {} +
Он будет правильно обрабатывать файлы и каталоги со встроенными пробелами, новыми строками или чем-то еще, и более эффективным!--8--> (т. е. быстрее), чем принято, и большинство, если не все другие ответы.
для тех, кто полагается на команды, кроме find, например
ls
:find . | grep "FooBar" | tr \n \0 | xargs -0 -I{} cp "{}" ~/foo/bar
find | perl -lne 'print quotemeta' | xargs ls -d
Я считаю, что это будет надежно работать для любого символа, кроме линейного канала (и я подозреваю, что если у вас есть линейные каналы в ваших именах файлов, то у вас есть проблемы похуже, чем это). Он не требует GNU findutils, просто Perl, поэтому он должен работать практически везде.
Я обнаружил, что следующий синтаксис работает хорошо для меня.
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 " в качестве одного аргумента.
имейте в виду, что большинство вариантов, обсуждаемых в других ответах, не являются стандартными на платформах, которые не используют утилиты GNU (Solaris, AIX, HP-UX, например). Смотрите POSIX спецификация для "стандартного" поведения xargs.
Я также нахожу поведение xargs, при котором он запускает команду хотя бы один раз, даже без ввода, чтобы быть неприятностью.
Я написал свою собственную частную версию xargs (xargl) для решения проблем пространств в именах (только строки по отдельности - хотя найти ... - print0' и 'xargs -0' комбинация довольно аккуратная, учитывая, что имена файлов не могут содержать символы ASCII NUL '\0'. Мой xargl не так полон, как это должно быть, чтобы стоило публиковать - тем более, что GNU имеет средства, которые по крайней мере так же хороши.
Если версии 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
Я создал небольшой портативный скрипт-оболочку под названием " xargsL "вокруг" xargs", который решает большинство проблем.
В отличие от xargs, xargsL принимает один путь на строку. Имена путей могут содержать любой символ, кроме (очевидно) новой строки или нулевых байтов.
в списке файлов не допускается и не поддерживается цитирование-ваши имена файлов могут содержать всевозможные пробелы , обратные косые черты, обратные кавычки, символы подстановки оболочки и т. п.-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 " failed!" >& 2' 0 if IFS= read -r first then { printf '%s\n' "$first" cat } | sed 's/./\&/g' | xargs ${1+"$@"} fi
поместите скрипт в какой-нибудь каталог в вашем $PATH и не забывайте к
$ chmod +x xargsL
скрипт там, чтобы сделать его исполняемым.
версия Perl bill_starr не будет хорошо работать для встроенных новых строк (только справляется с пробелами). Для тех, кто, например, на Solaris, где у вас нет инструментов GNU, может быть более полная версия (с использованием sed)...
find -type f | sed 's/./\&/g' | xargs grep string_to_find
отрегулируйте аргументы find и grep или другие команды по мере необходимости, но sed исправит ваши встроенные новые строки/пробелы/вкладки.
Я ответ Билла Стара немного изменено на Solaris:
find . -mtime +2 | perl -pe 's{^}{\"};s{$}{\"}' > ~/output.file
это поставит кавычки вокруг каждой строки. Я не использовал опцию "- l", хотя это, вероятно, поможет.
список файлов, который я собирался, хотя и может иметь ' -', но не новые строки. Я не использовал выходной файл с любыми другими командами, поскольку я хочу просмотреть то, что было найдено, прежде чем я просто начну массово удалять их через xargs.
С Bash (не POSIX) вы можете использовать процесс подстановки, чтобы получить текущую строку внутри переменной. Это позволяет использовать кавычки для экранирования специальных символов:
while read line ; do cp "$line" ~/bar ; done < <(find . | grep foo)
Я немного поиграл с этим, начал подумывать об изменении 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.
для меня, я пытался сделать что-то немного другое. Я хотел скопировать мой .txt файлы в мою папку tmp. Этот.имена файлов txt содержат пробелы и символы Апострофа. Это работает на моем Mac.
$ find . -type f -name '*.txt' | sed 's/'"'"'/\'"'"'/g' | sed 's/.*/"&"/' | xargs -I{} cp -v {} ./tmp/
вам может понадобиться каталог grep Foobar, например:
find . -name "file.ext"| grep "FooBar" | xargs -i cp -p "{}" .
если вы используете Bash, вы можете конвертировать stdout к массиву строк по
mapfile
:find . | grep "FooBar" | (mapfile -t; cp "${MAPFILE[@]}" ~/foobar)
преимущества:
- он встроенный, так что это быстрее.
- выполните команду со всеми именами файлов за один раз, так что это быстрее.
вы можете добавить другие аргументы к именам файлов. Ибо
cp
вы можете:find . -name '*FooBar*' -exec cp -t ~/foobar -- {} +
однако, некоторые команды не имеют таких особенность.
недостатки:
- возможно, не масштабируется хорошо, если слишком много имен файлов. (Предел? Я не знаю, но я тестировал с файлом списка 10 MB, который включает в себя 10000 + имен файлов без проблем, под Debian)
хорошо... кто знает, если Bash доступен на OS X?