[Linux] xargs를 사용하여 이름에 공백과 인용 부호가있는 파일을 복사하려면 어떻게합니까?


Answers

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

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

이것은 공백이있는 파일 이름과 디렉토리를 처리합니다. 대문자와 소문자가 구별되는 결과를 얻으려면 -name 을 사용할 수 있습니다.

주 : cp 전달 된 -- 플래그는 옵션으로 - 로 시작하는 파일을 처리하지 못하게합니다.

Question

나는 디렉토리 아래에있는 파일들을 복사하려고 시도하고있다. 그리고 그 파일들의 수는 그들의 이름에 공백과 작은 따옴표를 가지고있다. 함께 xargs find grep 하려고하면 다음 오류가 발생합니다.

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

xargs의보다 강력한 사용에 대한 제안?

이것은 MacOS 10.5.3에서 BSD xargs 입니다.




bash (POSIX 아님)를 사용하면 프로세스 대체를 사용하여 변수 내에서 현재 행을 가져올 수 있습니다. 이렇게하면 따옴표를 사용하여 특수 문자를 이스케이프 할 수 있습니다.

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



솔라리스에서 약간 변형 된 Bill Star의 답변을 사용했습니다.

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

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

내가 가고있는 파일 목록에는 줄 바꿈이 아닌 '-'이있을 수 있습니다. xargs를 통해 대량으로 삭제하기 전에 발견 된 내용을 검토하기 위해 다른 명령과 함께 출력 파일을 사용하지 않았습니다.




명령에 의존하는 사람들을 위해서, 예를 들어 ls :

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



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

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



이 작업을 조금 해보고 xargs 수정을 고려하기 시작했습니다. 여기서 말하는 유즈 케이스의 경우 파이썬에서 간단한 구현이 더 좋은 아이디어라는 것을 깨달았습니다. 한 가지는 80 줄 정도의 코드를 사용하는 것이 무엇이 진행되고 있는지 파악하기 쉽다는 것이며, 다른 동작이 필요한 경우에는 새로운 스크립트로 해킹하는 것보다 짧은 시간에 새 스크립트를 해킹 할 수 있다는 것입니다 어딘가에 같은 응답.

https://github.com/johnallsup/jda-misc-scripts/blob/master/yargshttps://github.com/johnallsup/jda-misc-scripts/blob/master/zargs.py

작성된 yargs (및 python3이 설치되어 있음)를 입력하면

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

한 번에 203 개의 파일을 복사 할 수 있습니다. (여기서 203은 물론 자리 표시 자일 뿐이며 203과 같은 이상한 번호를 사용하면이 번호가 다른 의미가 없음을 분명히 알 수 있습니다.)

파이썬을 필요로하지 않고 더 빠른 것을 원한다면 zargs와 yargs를 프로토 타입으로 사용하고 C ++이나 C로 다시 작성하십시오.




이 방법은 Mac OS X Lion 10.7.5에서 작동합니다.

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

편집 : 또한 방금 게시 한 정확한 구문을 테스트했습니다. 10.7.5에서도 잘 돌아갔습니다.




위의 perl 버전은 포함 된 개행 문자 (공백 문자에만 해당)에서만 제대로 작동하지 않습니다. 예를 들어 gnu 도구가없는 solaris의 경우보다 완전한 버전이 (sed를 사용하여)있을 수 있습니다 ...

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

find 및 grep 인수 또는 다른 명령을 필요에 따라 조정하지만 sed는 포함 된 줄 바꿈 / 공백 / 탭을 수정합니다.




나는 다음 구문이 나를 위해 잘 작동한다는 것을 발견했다.

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"로 전달합니다.

Bill Starr Fri, 2009 년 1 월 23 일 5:40 pm EST




어리석은 비 GNU 버전의 사용자에게는 파일 이름에 아포스트로피가있는 경우 Bill Starr의 솔루션이 작동하지 않습니다. rjb1도 같은 문제가 있다고 생각합니다. 테스트와 함께 복제 할 수는 없습니다. Carl Yamamoto-Furst의 버전이 작동합니다.




시스템의 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에서 테스트 됨




bash를 사용하고 있다면, stdoutmapfile 에 의해 행의 배열로 변환 할 수 있습니다.

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

이점은 다음과 같습니다.

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

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

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

단점 :

  • 파일 이름이 너무 많으면 확장되지 않을 수 있습니다. (한계는 모르겠지만 데비안에서는 문제없이 10000 개 이상의 파일 이름을 포함하는 10MB 목록 파일로 테스트했습니다)

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




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

나는 이것이 줄 바꿈을 제외한 어떤 문자에서도 안정적으로 작동 할 것이라고 믿는다. (그리고 나는 네가 파일 이름에 줄 바꿈을했다면 이보다 더 나쁜 문제가있다). GNU findutils는 Perl 만 필요로하지 않으므로 어느 곳에서나 작동합니다.




나는 똑같은 문제에 부딪쳤다. 여기 내가 어떻게 해결 했는가?

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

나는 sed 를 사용하여 각 입력 행을 동일한 행으로 대체했지만 큰 따옴표로 묶었습니다. sed 맨 페이지에서 " ... 대체에 나타나는 앰퍼샌드 (``& '')는 RE ...와 일치하는 문자열로 대체됩니다. -이 경우 .* , 전체 행.

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




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

xargs의 동작은 입력이 없더라도 명령을 적어도 한 번 실행하면 불쾌하다고 생각합니다.

나는 이름에서 공백의 문제를 다루기 위해 xargs (xargl)의 개인용 버전을 작성했습니다 (새 줄만 분리합니다 - 'find ... -print0'및 'xargs -0'조합은 파일 이름을 사용할 수 없다는 점을 감안할 때 상당히 깔끔합니다). ASCII NUL '\ 0'문자가 포함되어 있습니다. xargl은 퍼블리싱 할 가치가 있어야 할만큼 완벽하지는 않습니다 - 특히 GNU는 최소한 좋은 기능을 갖추고 있습니다.




다음은 find ( xargs 또는 cp GNU 특정 확장을 필요로하지 않는 포터블 (POSIX) 솔루션입니다 :

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

공백, 줄 바꿈 등이 포함 된 파일 및 디렉토리를 올바르게 처리하며, 다른 모든 답변의 대부분은 아니지만 대부분의 경우보다 효율적 (즉, 빠름 )합니다.




find에서 -print0 옵션을 사용하여 xargs에 --null 명령 행 옵션을 사용하십시오.




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

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




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

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



나를 위해, 나는 조금 다른 것을하려고 노력하고 있었다. 내 tmp 파일을 내 tmp 폴더에 복사하고 싶었습니다. txt 파일 이름에는 공백과 아포스트로피 문자가 포함되어 있습니다. 이건 내 맥에서 일했다.

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



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

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



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

grep--null 지원 --null , xargs 가 Leopard에서 -0 지원하는지 여부는 모르지만 GNU에서는 모두 좋습니다.