Как я могу использовать 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 204

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--> (т. е. быстрее), чем принято, и большинство, если не все другие ответы.

изучите использование параметра командной строки --null для xargs с параметром-print0 в find.

для тех, кто полагается на команды, кроме 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?