Как я могу использовать 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
.
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
Я считаю, что это будет надежно работать для любого символа, кроме line-feed (и я подозреваю, что если у вас есть line-Feed в ваших именах файлов, то у вас есть худшие проблемы, чем это). Он не требует 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 принимает один путь на строку. Имена путей могут содержать любой символ, кроме (очевидно) байтов newline или 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 " 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?