xargs를 사용하여 이름에 공백과 따옴표가있는 파일을 복사하려면 어떻게해야합니까?


232

디렉토리 아래에 많은 파일을 복사하려고하는데 많은 파일 이름에 공백과 작은 따옴표가 있습니다. findgrep함께 문자열을 시도 xargs하면 다음 오류가 발생합니다.

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

보다 강력한 xargs 사용법에 대한 제안 사항이 있습니까?

이것은 BSD와 Mac OS X 10.5.3 (Leopard)에 xargs있습니다.


2
작은 따옴표가 포함 된 파일 이름이 포함 된 GNU xargs 오류 메시지는 "xargs : unmatched single quote; 기본적으로 따옴표는 -0 옵션을 사용하지 않는 한 xargs에 특별합니다"라는 오류 메시지가 더 유용합니다.
Steve Jessop

3
GNU xargs에는 --delimiter옵션 ( -d)도 있습니다. \n구분 기호로 사용해보십시오. xargs공백이있는 줄을 여러 단어 / 인수로 분리 하는 것을 방지 합니다.
MattBianco

답변:


199

이 모든 것을 하나의 find명령 으로 결합 할 수 있습니다 .

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

공백이있는 파일 이름과 디렉토리를 처리합니다. -name대소 문자를 구분하여 결과를 얻을 수 있습니다 .

참고 : --플래그가 옵션으로 cp시작하는 파일을 처리하지 못하도록 전달되었습니다 -.


70
xargs는 일반적으로 매번 하나의 인수로 1000 번 호출하는 것보다 매번 200 개의 인수로 실행 파일을 5 번 호출하는 것이 더 빠르기 때문에 xargs를 사용합니다.
tzot

12
Chris Jester-Young의 대답은 "좋은 대답"이어야합니다 ... BTW 파일 이름이 "-"로 시작하면이 솔루션이 작동하지 않습니다. 적어도 cp 다음에는 "-"가 필요합니다.
Keltia

11
829 개가 넘는 파일에서 "find -exec"방법은 26 초가 걸리고 "find -print0 | xargs --null"방법은 0.7 초입니다. 확연히 다른.
피터 포터

7
@tzot 늦게 언급했지만 어쨌든 xargs설명하는 문제를 해결하는 데 필요하지는 않으며 find이미 -exec +문장 부호로 지원합니다 .
jlliagre

3
공간을 다루는 방법에 대한 질문에 대답하지 않음
Ben Glasser

117

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

나는 여부를 모르는 grep지원 --null,도 여부를 xargs지원 -0, 레오파드,하지만 GNU에 모두 좋다.


1
Leopard는 "-Z"(GNU grep)를 지원하며 물론 find (1) 및 xargs (1)는 "-0"을 지원합니다.
Keltia

1
OS X 10.9에서 grep -{z|Z}"zgrep로 작동"(압축 해제)을 의미하고 의도 한 "각 파일 이름 뒤에 0 바이트를 인쇄"하지 않습니다. grep --null후자를 달성하기 위해 사용하십시오 .
bassim

4
무슨 일이야 find . -name 'FooBar' -print0 | xargs -0 ...?
Quentin Pradet

1
@QuentinPradet 분명히 "FooBar"와 같은 고정 문자열 -name이거나 -path제대로 작동합니다. OP는 grep정규 표현식을 사용하여 목록을 필터링하려고하기 때문에을 사용하도록 지정했습니다 .
Chris Jester-Young

1
@ Hi-Angel 이것이 바로 와 함께 사용 하는 이유 xargs -0 입니다 find -print0 . 후자는 NUL 종료자를 사용하여 파일 이름을 인쇄하고 전자는 그런 식으로 파일을 수신합니다. 왜? Unix의 파일 이름에는 줄 바꿈 문자가 포함될 수 있습니다. 그러나 NUL 문자는 포함 할 수 없습니다.
Chris Jester-Young

92

원래 포스터가 원하는 것을 수행하는 가장 쉬운 방법은 구분 기호를 공백에서 다음과 같이 줄 끝 문자로 변경하는 것입니다.

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

4
이 anwser는 간단하고 효과적이며 요점까지 직설적입니다. xargs에 설정된 기본 구분 기호가 너무 넓어 OP가 수행하려는 작업에 맞게 좁혀 야합니다. 나는 cygwin을 제외하고는 비슷한 일을하는 것과 똑같은 문제에 부딪 쳤기 때문에 이것을 직접 알고 있습니다. xargs 명령에 대한 도움말을 읽었을 때 두통을 피할 수 있었지만 솔루션이 문제를 해결했습니다. 감사 ! (예, OP는 사용하지 않는 BSD xargs를 사용하여 MacOS에 있었지만 xargs "-d"매개 변수가 모든 버전에 존재하기를 바랍니다.)
Etienne Delavennat

7
좋은 답변이지만 Mac에서는 작동하지 않습니다. 대신 파일 이름을 따옴표로 sed -e 's_\(.*\)_"\1"_g'
묶기

10
이것이 정답입니다. 문제는에 대한 사용 xargs이었습니다.
Mohammad Alhashash

2
나는 얻는다xargs: illegal option -- d
nehem

1
파일 이름이 많은 * nix 시스템에서 줄 바꿈 문자를 포함 할 수 있음을 지적 할 가치가 있습니다. 당신은 야생에서 이것에 부딪 칠 것 같지 않지만, 신뢰할 수없는 입력에서 쉘 명령을 실행하고 있다면 이것이 문제가 될 수 있습니다.
Soren Bjornstad

71

"cp"를 여러 번 실행하지 않으므로보다 효율적입니다.

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

1
이것은 나를 위해 작동하지 않았습니다. 그것은 당신이 찾은 것에 cp ~ / foo / bar를 시도했지만 반대는 아니 었습니다
Shervin Asgari

13
cp에 대한 -t 플래그는 GNU 확장 인 AFAIK이며 OS X에서는 사용할 수 없습니다. 그러나이 답변은이 답변에 표시된대로 작동합니다.
metamatt

2
Linux를 사용하고 있습니다. '-t'스위치에 감사드립니다. 그것이 내가 잃어버린 것 :-)
Vahid Pazirandeh

59

나는 같은 문제에 부딪쳤다. 내가 해결 한 방법은 다음과 같습니다.

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

내가 사용하는 sed같은 라인 입력의 각 라인을 대체하는 것이 아니라 큰 따옴표로 둘러싸여 있습니다. 로부터 sedman 페이지, " ... 앰퍼샌드 (``& '') 교체에 나타나는은 RE를 ... 일치하는 문자열로 대체 이 경우, -" .*전체 라인.

이것은 xargs: unterminated quote오류를 해결합니다 .


3
나는 창문에 있고 gnuwin32를 사용 sed s/.*/\"&\"/하고 있기 때문에 작동 시켜야 했습니다.
Pat

그렇습니다. 아마도 "sed도 따옴표를 인용하지 않는 한 파일 이름을 in으로 처리하지 않을 것입니다 .
artfulrobot

사용 sed은 천재적이며 현재로서는 문제를 다시 쓰지 않고 올바른 해결책입니다!
entonio 2016 년

53

이 방법은 Mac OS X v10.7.5 (Lion)에서 작동합니다.

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

또한 게시 한 정확한 구문을 테스트했습니다. 10.7.5에서도 잘 작동했습니다.


4
이것은 작동하지만 -I묵시적으로 -L 1(매뉴얼이라고 함 ) cp 명령이 파일 당 한 번 실행되고 있음을 의미합니다.
artfulrobot

xargs -J % cp % <destination dir> OSX에서 더 효율적일 수 있습니다.
Walker D

3
죄송합니다. 잘못되었습니다. 먼저 TO가 피하고 싶었던 오류를 정확하게 생성합니다. arround xargs의 "기본적으로 따옴표는 특별합니다"를 사용 find ... -print0하고 xargs -0작동 해야합니다 . 둘째, 일반적으로 공백과 특수 문자로부터 보호하기 위해 xargs에 전달 된 명령 '{}'에는 사용 하지 않습니다 {}.
Andreas Spindler

3
미안 Andreas Spindler, 나는 xargs에 익숙하지 않으며 실험 후이 줄을 발견했습니다. 댓글을 달고 의견을 말한 대부분의 사람들에게 효과가있는 것 같습니다. 어떤 종류의 오류가 발생하는지 조금 더 자세히 설명해 주시겠습니까? 또한 더 정확하다고 생각되는 정확한 입력을 게시 하시겠습니까? 감사합니다.
the_minted

12

그냥 사용하지 마십시오 xargs. 깔끔한 프로그램이지만 잘 어울리지 않습니다.find 사소한 경우 어울리지 .

여기에 휴대용 (POSIX) 솔루션, 즉 필요로하지 않는 하나 find, xargs또는 cpGNU의 특정 확장 :

find . -name "*FooBar*" -exec sh -c 'cp -- "$@" ~/foo/bar' sh {} +

+더 평소 대신 결말에 주목하십시오.; .

이 솔루션 :

  • 공백, 개행 또는 이국적인 문자가 포함 된 파일 및 디렉토리를 올바르게 처리합니다.

  • GNU 툴킷을 제공하지 않는 유닉스 및 리눅스 시스템에서도 작동합니다.

  • xargs훌륭하고 유용한 프로그램을 사용하지 않지만 find출력 을 올바르게 처리하기 위해서는 너무 많은 조정 및 비표준 기능이 필요 합니다.

  • 또한 허용되는 것보다 더 효율적 이며 ( 빠른 읽기 ) 다른 답변 모두는 아닙니다.

또한 다른 답변이나 의견에 언급 된 내용에도 불구하고 인용문 {}은 쓸모가 없습니다 (이국적인 fish껍질을 사용하지 않는 한 ).



1
@PeterMortensen 아마도 결말 플러스를 간과 할 것입니다. 오버 헤드없이 find할 수 있습니다 xargs.
jlliagre

8

-print0 옵션이있는 xargs에 대해 --null 명령 줄 옵션을 사용하십시오.



6
find | perl -lne 'print quotemeta' | xargs ls -d

줄 바꿈을 제외한 모든 문자에 이것이 안정적으로 작동한다고 생각합니다 (파일 이름에 줄 바꿈이 있으면 이것보다 더 나쁜 문제가 있다고 생각합니다). GNU findutils가 필요하지 않으며 Perl 만 필요하므로 어느 곳에서나 잘 작동합니다.


파일 이름에 줄 바꿈이 가능합니까? 들어 본 적이 없어
mtk

2
실제로 그렇습니다. 예를 들어mkdir test && cd test && perl -e 'open $fh, ">", "this-file-contains-a-\n-here"' && ls | od -tx1
mavit

1
|perl -lne 'print quotemeta'내가 찾던 것입니다. 대신 때문에 다른 게시물은 나를 여기에 도움이되지 않았다 find내가 사용하는 데 필요한 grep -rl크게에만 악성 코드에 감염된 사람에 PHP 파일의 수를 줄일 수 있습니다.
Marcos

공백이있는 파일을 파이프 라이닝하는 일반적인 솔루션 덕분에 perl과 quotemeta가 print0 / -0보다 훨씬 일반적입니다.
bmike

5

다음 구문이 저에게 효과적이라는 것을 알았습니다.

find /usr/pcapps/ -mount -type f -size +1000000c | perl -lpe ' s{ }{\\ }g ' | xargs ls -l | sort +4nr | head -200

이 예에서는 "/ usr / pcapps"에 마운트 된 파일 시스템에서 1,000,000 바이트가 넘는 최대 200 개의 파일을 찾고 있습니다.

"find"와 "xargs"사이의 Perl 행 라이너는 각 공백을 이스케이프 / 따옴표로 묶어 "xargs"는 공백이 포함 된 모든 파일 이름을 단일 인수로 "ls"에 전달합니다.


3

프레임 챌린지 — xargs 사용법을 묻습니다. 답은 xargs를 사용하지 않는 것입니다. 필요하지 않기 때문입니다.

에 의해 코멘트는user80168 모든 파일에 대해 CP를 호출하지 않고, CP와 직접이 작업을 수행하는 방법을 설명합니다 :

find . -name '*FooBar*' -exec cp -t /tmp -- {} +

이것은 다음과 같은 이유로 작동합니다.

  • cp -t플래그의 시작 부분 대상 디렉토리를 제공 할 수 있습니다 cp보다는 끝 부분보다. 보낸 사람 man cp:
   -t, --target-directory=DIRECTORY
         copy all SOURCE arguments into DIRECTORY
  • --플래그는 이야기 cp로 시작하는 파일 있도록 파일 이름이 아닌 플래그와 후 모든 것을 해석 -또는 --혼동하지 마십시오 cp; -/ --문자는로 해석되고 cp다른 특수 문자는 쉘로 해석 되기 때문에 여전히 필요합니다 .

  • find -exec command {} +변형은 본질적으로 xargs를 같은 않습니다. 보낸 사람 man find:

   -exec command {} +                                                     
         This  variant  of the -exec action runs the specified command on
         the selected files, but the command line is built  by  appending
         each  selected file name at the end; the total number of invoca‐
         matched  files.   The command line is built in much the same way
         that xargs builds its command lines.  Only one instance of  `{}'
         is  allowed  within the command, and (when find is being invoked
         from a shell) it should be quoted (for example, '{}') to protect
         it  from  interpretation  by shells.  The command is executed in
         the starting directory.  If any invocation  returns  a  non-zero
         value  as exit status, then find returns a non-zero exit status.
         If find encounters an error, this can sometimes cause an immedi‐
         ate  exit, so some pending commands may not be run at all.  This
         variant of -exec always returns true.

이것을 find에서 직접 사용하면 파이프 또는 쉘 호출이 필요하지 않으므로 파일 이름의 불쾌한 문자에 대해 걱정할 필요가 없습니다.


놀라운 발견, 나는 몰랐다! !! "-exec 유틸리티 [argument ...] {} + -exec와 동일하지만``{} ''은 (는) 유틸리티를 호출 할 때마다 가능한 한 많은 경로 이름으로 대체됩니다.이 동작은 xargs (1의 동작과 유사합니다. ). " BSD 구현에서.
conny

2

다른 답변에서 논의 된 대부분의 옵션은 GNU 유틸리티를 사용하지 않는 플랫폼 (예 : Solaris, AIX, HP-UX)에서는 표준이 아닙니다. '표준'xargs 동작에 대해서는 POSIX 사양을 참조하십시오 .

또한 xargs가 입력을하지 않아도 명령을 한 번 이상 실행하여 번거 로움을주는 동작을 찾습니다.

나는 공간 이름의 공백 문제를 처리하기 위해 xargs (xargl)의 개인 버전을 작성했습니다 (파일 이름은 'find ... -print0'과 'xargs -0'조합이 깔끔하지만 깔끔합니다. ASCII NUL '\ 0'문자가 포함되어 있습니다 .xargl은 게시 할 가치가있을만큼 완벽하지는 않습니다. 특히 GNU에는 최소한 우수한 기능이 있습니다.


2
GitHub 또는 그렇지 않은 경우
Corey Goldberg

@CoreyGoldberg : 그때는 그렇지 않은 것 같아요.
Jonathan Leffler

POSIX findxargs처음 에는 필요하지 않으며 11 년 전에 이미 적용되었습니다.
jlliagre

2

POSIX가 아닌 Bash를 사용하면 프로세스 대체를 사용하여 변수 내부의 현재 줄을 가져올 수 있습니다. 따옴표를 사용하여 특수 문자를 이스케이프 할 수 있습니다.

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

2

나를 위해, 나는 조금 다른 것을하려고했습니다. .txt 파일을 tmp 폴더에 복사하고 싶었습니다. .txt 파일 이름은 공백과 아포스트로피 문자를 포함합니다. 이것은 내 Mac에서 작동했습니다.

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

1

시스템에 발견하고 xarg 버전을 지원하지 않는 경우 -print0-0(예를 들어, AIX의 발견 및 xargs를위한) 스위치 당신이 정말보고 코드를 사용할 수 있습니다 :

 find . -name "*foo*" | sed -e "s/'/\\\'/g" -e 's/"/\\"/g' -e 's/ /\\ /g' | xargs cp /your/dest

여기서 sed는 xargs의 공백과 따옴표를 이스케이프 처리합니다.

AIX 5.3에서 테스트


1

대부분의 문제를 해결하는 "xargs"주위에 "xargsL"이라는 작은 휴대용 래퍼 스크립트를 만들었습니다.

xargs와 달리 xargsL은 한 줄에 하나의 경로 이름을 허용합니다. 경로명에는 개행 또는 NUL 바이트를 제외한 모든 문자가 포함될 수 있습니다.

파일 목록에 인용이 허용되거나 지원되지 않습니다. 파일 이름에는 모든 종류의 공백, 백 슬래시, 백틱, 쉘 와일드 카드 문자 등이 포함될 수 있습니다. xargsL은 문자를 리터럴 문자로 처리합니다.

추가 보너스 기능으로, 입력이 없으면 xargsL은 명령을 한 번 실행 하지 않습니다 !

차이점에 유의하십시오.

$ true | xargs echo no data
no data

$ true | xargsL echo no data # No output

xargsL에 주어진 모든 인수는 xargs로 전달됩니다.

"xargsL"POSIX 쉘 스크립트는 다음과 같습니다.

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

스크립트를 실행 가능하게 만듭니다.


1

bill_starr의 Perl 버전 은 임베드 된 개행에 적합하지 않습니다 (공백 만 처리). 예를 들어 GNU 도구가없는 Solaris의 경우보다 완전한 버전이 될 수 있습니다 (sed 사용).

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

find 및 grep 인수 또는 필요한 다른 명령을 조정하십시오. 그러나 sed는 포함 된 개행 / 공간 / 탭을 수정합니다.


1

Solaris에서 약간 수정 된 Bill Star의 답변을 사용했습니다 .

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

이렇게하면 각 줄에 따옴표가 붙습니다. 아마도 도움이 되겠지만 '-l'옵션을 사용하지 않았습니다.

내가 가고있는 파일 목록에는 '-'가있을 수 있지만 줄 바꿈은 없습니다. xargs를 통해 대량으로 삭제하기 전에 찾은 내용을 검토하고 싶기 때문에 다른 명령과 함께 출력 파일을 사용하지 않았습니다.


1

나는 이것을 조금 가지고 놀고 xargs를 수정하기 시작했고, 우리가 여기서 이야기하고있는 유스 케이스의 경우, 파이썬에서 간단한 재 구현이 더 나은 아이디어라는 것을 깨달았습니다.

한 가지로, 전체에 ~ 80 줄의 코드가 있으면 무슨 일이 일어나고 있는지 쉽게 알 수 있으며 다른 동작이 필요한 경우 걸리는 시간보다 짧은 시간 안에 새로운 스크립트로 해킹 할 수 있습니다 스택 오버플로와 같은 어딘가에 대한 답변.

참조 https://github.com/johnallsup/jda-misc-scripts/blob/master/yargshttps://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과 같은 이상한 숫자를 사용하면이 숫자에 다른 의미가 없음을 알 수 있습니다.)

파이썬을 필요로하지 않고 무언가를 더 빨리 원한다면 zargs와 yargs를 프로토 타입으로 사용하고 C ++ 또는 C로 다시 작성하십시오.


0

Foobar 디렉토리를 다음과 같이 grep해야 할 수도 있습니다.

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

1
매뉴얼 페이지에 따라 -i더 이상 사용되지 않으며 -I대신 사용해야합니다.
Acumenus

-1

Bash를 사용하는 경우 stdout 을 다음과 같이 행 배열 로 변환 할 수 있습니다 mapfile.

find . | grep "FooBar" | (mapfile -t; cp "${MAPFILE[@]}" ~/foobar)

이점은 다음과 같습니다.

  • 내장되어 있으므로 더 빠릅니다.
  • 한 번에 모든 파일 이름으로 명령을 실행하면 더 빠릅니다.
  • 파일 이름에 다른 인수를 추가 할 수 있습니다. 에 대한cp 다음을 수행 할 수도 있습니다.

    find . -name '*FooBar*' -exec cp -t ~/foobar -- {} +
    

    그러나 일부 명령에는 이러한 기능이 없습니다.

단점 :

  • 파일 이름이 너무 많으면 확장 성이 떨어질 수 있습니다. (제한은? 모르겠지만 데비안에서 10000 + 파일 이름을 포함하는 10MB 목록 파일로 테스트했습니다.)

글쎄 ... 누가 OS X에서 Bash를 사용할 수 있는지 알고 있습니까?

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.