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

Я пытаюсь скопировать кучу файлов в каталог, и в именах некоторых файлов есть пробелы и одинарные кавычки. Когда я пытаюсь связать вместе 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 ]

5 голосов
/ 24 января 2009

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

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

2 голосов
/ 02 октября 2008

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

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

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

2 голосов
/ 08 октября 2015

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

$ find . -type f -name '*.txt' | sed 's/'"'"'/\'"'"'/g' | sed 's/.*/"&"/'  | xargs -I{} cp -v {} ./tmp/
1 голос
/ 05 августа 2015

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

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

См. https://github.com/johnallsup/jda-misc-scripts/blob/master/yargs и https://github.com/johnallsup/jda-misc-scripts/blob/master/zargs.py.

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

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

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

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

1 голос
/ 27 марта 2015

Если версии 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 голос
/ 01 января 2014

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

while read line ; do cp "$line" ~/bar ; done < <(find . | grep foo)
1 голос
/ 31 октября 2010

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

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

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

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

1 голос
/ 17 июня 2009

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

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

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

1 голос
/ 17 марта 2018

Я создал небольшой переносимый скрипт-обертку под названием «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+"$@"}
fi

Поместите скрипт в какой-то каталог в вашем $ PATH и не забудьте

$ chmod +x xargsL

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

0 голосов
/ 26 июня 2013

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

find . -name "file.ext"| grep "FooBar" | xargs -i cp -p "{}" .
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...