Programing

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

lottogame 2020. 4. 20. 19:15
반응형

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


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

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

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

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


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

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

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

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


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

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


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

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

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

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

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

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

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

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


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

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

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


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

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

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

+더 일반적인 대신 결말에 유의하십시오 ;.

이 솔루션 :

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

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

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

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

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


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


찾기 이외의 명령에 의존하는 사람들의 경우 : 예 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

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

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


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

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

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


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

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

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

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

시스템에 발견하고 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에서 테스트


대부분의 문제를 해결하는 "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

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


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

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

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


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

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

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

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


나는 이것을 조금 가지고 놀고 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로 다시 작성하십시오.


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

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

프레임 챌린지 — 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에서 직접 사용하면 파이프 또는 쉘 호출이 필요하지 않으므로 파일 이름의 불쾌한 문자에 대해 걱정할 필요가 없습니다.


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

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

이점은 다음과 같습니다.

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

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

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

단점 :

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

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

참고 : https://stackoverflow.com/questions/143171/how-can-i-use-xargs-to-copy-files-that-have-spaces-and-quotes-in-their-names

반응형