it-swarm.com.ru

Как я могу использовать xargs для копирования файлов с пробелами и кавычками в именах?

Я пытаюсь скопировать кучу файлов в каталог, и в некоторых файлах есть пробелы и одинарные кавычки. Когда я пытаюсь связать вместе find и grep с xargs, я получаю следующую ошибку:

find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote

Какие-нибудь предложения для более надежного использования xargs?

Это включено Mac OS X 10.5. (Leopard) с BSD xargs.

216
Drew Stephens

Вы можете объединить все это в одну команду find:

find . -iname "*foobar*" -exec cp -- "{}" ~/foo/bar \;

Это будет обрабатывать имена файлов и каталогов с пробелами в них. Вы можете использовать -name для получения результатов с учетом регистра.

Примечание. Флаг --, передаваемый cp, не позволяет обрабатывать файлы, начинающиеся с - в качестве параметров.

191
godbyk

find . -print0 | grep --null 'FooBar' | xargs -0 ...

Я не знаю, поддерживает ли grep--null, и поддерживает ли xargs-0, на Leopard, но на GNU это все хорошо.

114
Chris Jester-Young

Самый простой способ сделать то, что хочет оригинальный плакат, - это изменить разделитель с любого пробела на символ конца строки, например так:

find whatever ... | xargs -d "\n" cp -t /var/tmp
76
user87601

Это более эффективно, так как не запускает "cp" несколько раз:

find -name '*FooBar*' -print0 | xargs -0 cp -t ~/foo/bar
69
Tometzky

Я столкнулся с той же проблемой. Вот как я это решил:

find . -name '*FoooBar*' | sed 's/.*/"&"/' | xargs cp ~/foo/bar

Я использовал sed, чтобы заменить каждую строку ввода одной строкой, но в двойных кавычках. На странице руководства sed "... амперсанд (` & & ''), появляющийся при замене, заменяется строкой, соответствующей RE ... "- в данном случае .*, вся линия.

Это решает ошибку xargs: unterminated quote.

55
oyouareatubeo

Этот метод работает на Mac OS X v10.7.5 (Lion):

find . | grep FooBar | xargs -I{} cp {} ~/foo/bar

Я также проверил точный синтаксис, который вы разместили. Это также хорошо работало на 10.7.5.

50
the_minted

Только не используйте xargs. Это аккуратная программа, но она не подходит для find, когда сталкивается с нетривиальными случаями.

Вот портативное (POSIX) решение, то есть такое, которое не требует find, xargs или cp GNU специальных расширений:

find . -name "*FooBar*" -exec sh -c 'cp -- "[email protected]" ~/foo/bar' sh {} +

Обратите внимание на окончание + вместо более обычного ;.

Это решение:

  • правильно обрабатывает файлы и каталоги со встроенными пробелами, символами новой строки или любыми экзотическими символами.

  • работает в любой системе Unix и Linux, даже в тех, которые не предоставляют инструментарий GNU.

  • не использует xargs, которая является хорошей и полезной программой, но требует слишком много настроек и нестандартных функций для правильной обработки вывода find.

  • также более эффективно (читать быстрее), чем принятые, и большинство, если не все другие ответы.

Также обратите внимание, что несмотря на то, что указано в некоторых других ответах или комментариях, цитирование {} бесполезно (если вы не используете экзотическую fishShell).

11
jlliagre

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

8
Shannon Nelson

Для тех, кто полагается на команды, кроме find, например ls:

find . | grep "FooBar" | tr \\n \\0 | xargs -0 -I{} cp "{}" ~/foo/bar
8
Aleksandr Guidrevitch

Я обнаружил, что следующий синтаксис работает хорошо для меня.

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" в качестве единственного аргумента.

5
bill_starr
find | Perl -lne 'print quotemeta' | xargs ls -d

Я считаю, что это будет работать надежно для любого символа, кроме перевода строки (и я подозреваю, что если у вас есть перевод строки в именах файлов, то у вас проблемы хуже, чем этот). Он не требует GNU findutils, только Perl, поэтому он должен работать практически везде.

5
mavit

Я пытался сделать что-то немного другое. Я хотел скопировать мои файлы .txt в мою папку TMP. Имена файлов .txt содержат пробелы и символы апострофа. Это сработало на моем Mac.

$ find . -type f -name '*.txt' | sed 's/'"'"'/\'"'"'/g' | sed 's/.*/"&"/'  | xargs -I{} cp -v {} ./tmp/
2
Moises

С Bash (не POSIX) вы можете использовать подстановку процесса, чтобы получить текущую строку внутри переменной. Это позволяет вам использовать кавычки для экранирования специальных символов:

while read line ; do cp "$line" ~/bar ; done < <(find . | grep foo)
2
StackedCrooked

Помните, что большинство опций, обсуждаемых в других ответах, не являются стандартными на платформах, которые не используют утилиты GNU (например, Solaris, AIX, HP-UX). Смотрите спецификацию POSIX для "стандартного" поведения xargs.

Я также считаю, что поведение xargs, при котором он запускает команду хотя бы один раз, даже без ввода, вызывает неудобства.

Я написал свою собственную частную версию xargs (xargl) для решения проблем с пробелами в именах (разделены только символами новой строки - хотя комбинация 'find ... -print0' и 'xargs -0' довольно аккуратна, поскольку имена файлов не могут содержат символы ASCII NUL '\ 0'. Мой xargl не настолько полный, как того стоило бы публиковать, тем более что в GNU есть средства, которые по крайней мере так же хороши ,.

2
Jonathan Leffler

Я создал небольшой переносимый скрипт-обертку под названием "xargsL" вокруг "xargs", который решает большинство проблем.

В отличие от xargs, xargsL принимает одно имя пути на строку. Имена путей могут содержать любой символ, кроме (очевидно) символа новой строки или байтов NUL.

В списке файлов цитирование не допускается и не поддерживается - ваши имена файлов могут содержать все виды пробелов, обратных косых черт, обратных черт, подстановочных знаков Shell и т. П. - xargsL будет обрабатывать их как буквенные символы, без вреда для себя.

В качестве дополнительной бонусной функции xargsL не выполнит команду один раз, если нет ввода!

Обратите внимание на разницу:

$ true | xargs echo no data
no data

$ true | xargsL echo no data # No output

Все аргументы, переданные xargsL, будут переданы xargs.

Вот сценарий "xargsL" POSIX Shell:

#! /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

скрипт, чтобы сделать его исполняемым.

1
Guenther Brunthaler

Я использовал ответ Билла Стар слегка измененный в Solaris:

find . -mtime +2 | Perl -pe 's{^}{\"};s{$}{\"}' > ~/output.file

Это поместит кавычки вокруг каждой строки. Я не использовал опцию '-l', хотя это, вероятно, помогло бы.

Список файлов, который я собирался, хотя мог иметь "-", но не переводы строк. Я не использовал выходной файл с какими-либо другими командами, так как хочу просмотреть то, что было найдено, прежде чем я просто начну массово удалять их с помощью xargs.

1
Carl Yamamoto-Furst

Я немного поиграл с этим, начал размышлять над модификацией xargs и понял, что для случая использования, о котором мы здесь говорим, простая переопределение в Python - лучшая идея.

Во-первых, наличие ~ 80 строк кода для всего этого означает, что легко понять, что происходит, и если требуется другое поведение, вы можете просто взломать его в новый скрипт за меньшее время, чем требуется, чтобы получить ответ на что-то вроде переполнения стека.

Смотрите https://github.com/johnallsup/jda-misc-scripts/blob/master/yargs и https://github.com/johnallsup/jda-misc-scripts/ блоб/ведущий/zargs.py .

С yargs как написано (и Python 3 установлено) вы можете набрать:

find .|grep "FooBar"|yargs -l 203 cp --after ~/foo/bar

сделать копирование 203 файлов одновременно. (Здесь 203, конечно, просто заполнитель, и использование странного числа, такого как 203, дает понять, что это число не имеет другого значения.)

Если вы действительно хотите что-то более быстрое и без использования Python, возьмите zargs и yargs в качестве прототипов и перепишите в C++ или C.

1
John Allsup

версия Perl bill_starr не будет работать хорошо для встроенных строк новой строки (справляется только с пробелами). Для тех, например В Solaris, где у вас нет инструментов GNU, может быть более полная версия (с использованием sed) ...

find -type f | sed 's/./\\&/g' | xargs grep string_to_find

настройте аргументы find и grep или другие команды, как вам требуется, но sed исправит ваши встроенные символы новой строки/пробелов/табуляции.

1
rjb1

Если версии 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

1
Jan Ptáčník

Вам может понадобиться grep каталог Foobar, например:

find . -name "file.ext"| grep "FooBar" | xargs -i cp -p "{}" .
0
fred