xargs가 공백이 포함 된 파일 이름을 처리하도록 설정


252
$ ls *mp3 | xargs mplayer  

Playing Lemon.  
File not found: 'Lemon'  
Playing Tree.mp3.  
File not found: 'Tree.mp3'  

Exiting... (End of file)  

"Lemon Tree.mp3"파일에 공백이 포함되어 있으므로 xargs는 두 파일이라고 생각하기 때문에 명령이 실패합니다. find + xargs가 이와 같은 파일 이름으로 작동하도록 할 수 있습니까?


대신을 ls |grep mp3 |sed -n "7p"사용할 수 있습니다 echo "Lemon Tree.mp3".
Micha Wiedenmann


답변:


255

xargs명령은 공백 문자 (탭, 공백, 줄 바꾸기)를 구분 기호로 사용합니다. 다음과 -d같은 옵션 을 사용하여 줄 바꾸기 문자 ( '\ n')에 대해서만 범위를 좁힐 수 있습니다 .

ls *.mp3 | xargs -d '\n' mplayer

GNU xargs에서만 작동합니다. BSD 시스템의 경우 다음 -0과 같은 옵션을 사용하십시오 .

ls *.mp3 | xargs -0 mplayer

이 방법은 더 간단하며 GNU xargs에서도 작동합니다.


6
일반적인 사용을위한 최고의 답변! 이전 명령이 "찾기"가 아닌 경우에도 작동합니다.
nexayq

28
불행히도이 옵션은 OS X에서 사용할 수 없습니다.
Thomas Tempelmann

25
@Thomas OS X의 경우 플래그는 -E예를 들면 다음과 같습니다.xargs -E '\n'

30
OS X에서 -E '\ n'은 나에게 영향을 미치지 않았으며 레코드 구분 기호가 아닌 eofstr을 수정했을 때 기대하지도 않았습니다. 그러나 이전 명령이 '찾기'가 아니더라도 입력에서 find의 -print0 플래그 효과를 시뮬레이션하여 -0 플래그를 솔루션으로 사용할 수있었습니다. 예 : ls * mp3 | tr '\ n' '\ 0'| xargs -0 mplayer
biomiker

10
OS X의 경우 "brew install findutils"를 사용하면 -d 스위치 있는 "gxargs"명령이 제공 됩니다.
Tom De Leu

213

xargs 유틸리티는 표준 입력에서 공백, 탭, 줄 바꾸기 및 파일 끝으로 구분 된 문자열을 읽고 문자열을 인수로 사용하여 유틸리티를 실행합니다.

공백을 구분 기호로 사용하지 않으려 고합니다. 이는 xargs의 분리 문자를 변경하여 수행 할 수 있습니다. 매뉴얼에 따르면 :

 -0      Change xargs to expect NUL (``\0'') characters as separators,
         instead of spaces and newlines.  This is expected to be used in
         concert with the -print0 function in find(1).

같은 :

 find . -name "*.mp3" -print0 | xargs -0 mplayer

일곱 번째 mp3 재생에 관한 질문에 대답하기 위해; 실행하는 것이 더 간단합니다

 mplayer "$(ls *.mp3 | sed -n 7p)"

10
이것은 GNU find와 GNU를 사용 하고 있습니다 xargs. 해당 프로그램의 모든 버전이 해당 옵션을 지원하는 것은 아닙니다 (하지만 필요한 경우가 있음).
Jonathan Leffler

1
@JonathanLeffler s / GNU / FreeBSD / g; POSIX는 슬프게도 텍스트 파일의 NUL 문자를 두려워하고 아직 충분한 치료를받지 못했습니다 :-) 실제로 내 조언은 이식 할 수없는 옵션에 의지합니다.
Jens

6
그리고 맥 OS X (A BSD 유도체)가 find함께 -print0xargs함께 -0. 그러나 AFAIK, HP-UX, AIX 및 Solaris는 그렇지 않습니다 (그러나 나는 정정해야합니다 : HP-UX 11i는 그렇지 않았지만 Solaris 10은 그렇지 않았습니다. ). sed예를 들어로 '\0'대신 끝나는 '줄'을 사용하는 것은 어렵지 않으며 '\n'POSIX 2008 getdelim()을 사용하면 쉽게 관리 할 수 ​​있습니다.
Jonathan Leffler

2
목록 파일을 포함하는 파일 경로와 함께 사용하기위한 +1 + 1 트릭 : cat $ file_paths_list_file | 펄 -ne 's | \ n | \ 000 | g; print'| xargs -0 zip $ zip_package
요르단 게오르기 프

2
줄 바꿈을 NUL로 바꾸는 것이 좋습니다. GNU find 나 GNU xargs도없고 perl도없는 내장 시스템에서이 작업을 수행해야했지만 tr 명령을 사용하여 동일한 작업을 수행 할 수 있습니다. cat $ file_paths_list_file | tr '\ n' '\ 0'| xargs -0 du -hms
joensson


16

MacOS의 xargs에는 -d 옵션이 없으므로이 솔루션은 대신 -0을 사용합니다.

ls가 한 줄에 하나의 파일을 출력하도록 한 다음 개행을 널로 변환하고 xargs에 널을 분리 문자로 사용하도록 지시하십시오.

ls -1 *mp3 | tr "\n" "\0" | xargs -0 mplayer


8
find . -name 'Lemon*.mp3' -print0 | xargs 0 -i mplayer '{}' 

이것은 내 경우 공백이있는 다른 파일을 삭제하는 데 도움이되었습니다. mplayer에서도 작동합니다. 필요한 트릭은 따옴표입니다. (Linux Xubuntu 14.04 에서 테스트되었습니다 .)


7

Dick.Guertin의 답변 [1]은 파일 이름에서 공백을 이스케이프 처리 할 수있는 방법이 여기에 제안 된 다른 솔루션을 대체 할 수있는 귀중한 대안이라고 제안했습니다 (예 : 공백 대신 null 문자를 구분 기호로 사용). 그러나 더 간단 할 수 있습니다-당신은 정말로 독특한 캐릭터가 필요하지 않습니다. sed는 이스케이프 된 공백을 직접 추가 할 수 있습니다.

ls | grep ' ' | sed 's| |\\ |g' | xargs ...

또한 grep은 이름에 공백이있는 파일 원하는 경우에만 필요 합니다. 더 일반적으로 (예를 들어, 공백이있는 파일을 처리 할 때 일부는 공백이 아닌 경우) grep을 건너 뛰십시오.

ls | sed 's| |\\ |g' | xargs ...

그런 다음 파일 이름에 공백이 아닌 다른 공백 (예 : 탭)이있을 수 있습니다.

ls | sed -r 's|[[:blank:]]|\\\1|g' | xargs ...

즉, GNU sed 또는 최신 버전의 bsd sed와 같은 -r (확장 정규식)을 지원하는 sed가 있다고 가정합니다 (예 : FreeBSD 8 이전에 "-E"옵션의 철자를 사용한 FreeBSD와 호환성을 위해 -r & -E 모두 지원) 적어도 FreeBSD 11을 통해). 그렇지 않으면 기본 정규식 문자 클래스 대괄호 표현식을 사용하고 []구분 기호 에 공백과 탭 문자를 수동으로 입력 할 수 있습니다 .

[1]이 답변에 대한 의견이나 수정 사항으로 더 적합 할 수도 있지만 현재로서는 의견을 평가할만한 충분한 평판이 없으며 수정 사항 만 제안 할 수 있습니다. 후자의 형태는 (그렙없이) Dick.Guertin의 원래 답변을 변경하기 때문에 직접 편집하는 것은 적절하지 않습니다.


1
출력을 고려하지 않고 파일 이름을 지정하는 스크립트를 실행하는 미친 유닉스 남자
andrew lorien

4

ls | grep mp3 | sed -n "7p" | xargs -i mplayer {}

위 명령 에서 각 파일에 대해 새로 xargs호출 mplayer합니다. 이는 바람직하지 mplayer않지만 다른 대상에는 적합 할 수 있습니다.


1
기존 답변에 유용한 추가 기능이지만 mplayer각 파일마다 새로 호출 될 수 있습니다 . 예를 들어 시도하면 중요합니다 . ... | xargs -I{} mplayer -shuffle {}그럼에도 불구하고 이것은 완전히 결정적인 순서로 재생됩니다 -shuffle.

1
일반적으로 의도가 아닙니다. xargs는 대부분 파일 이름 목록 (쉬운 예 :)을 받아들이는 명령과 함께 사용되며 rm, 각 호출에 적합 할 수있는만큼의 파일 이름을 전달하려고 시도하며 필요한 경우 여러 호출로 분할합니다. 각 호출이 표시되는 명령 echo( 예 : (기본값)) 을 사용할 때 차이점을 확인할 수 있습니다 . seq 0 100000 | xargs첫 번째 줄에서 0에서 23695 (플랫폼 별이지만 내 시스템에서 발생하는 모든 숫자 ) 를 45539까지 인쇄합니다. 대부분의 명령에는 문제가되지 않습니다.

4

macOS 10.12.x (Sierra)에서 파일 이름 또는 하위 디렉토리에 공백이 있으면 다음을 사용할 수 있습니다.

find . -name '*.swift' -exec echo '"{}"' \; |xargs wc -l

2

(a) 레몬과 달리 숫자 7에 어떻게 연결되어 있는지, (b) 파일 이름에 줄 바꿈이 포함되어 있는지 여부와 파일 이름이 바뀌면 기꺼이 바꿀지 여부에 따라 다릅니다.

여러 가지 방법으로 처리 할 수 ​​있지만 그 중 일부는 다음과 같습니다.

mplayer Lemon*.mp3

find . -name 'Lemon*.mp3' -exec mplayer {} ';'

i=0
for mp3 in *.mp3
do
    i=$((i+1))
    [ $i = 7 ] && mplayer "$mp3"
done

for mp3 in *.mp3
do
    case "$mp3" in
    (Lemon*) mplayer "$mp3";;
    esac
done

i=0
find . -name *.mp3 |
while read mp3
do
    i=$((i+1))
    [ $i = 7 ] && mplayer "$mp3"
done

그만큼 read파일 이름이 개행 문자가 포함 된 경우 루프가 작동하지 않습니다; 다른 것들은 이름의 개행으로도 올바르게 작동합니다 (공백 제외). 내 돈을 위해 줄 바꿈이 포함 된 파일 이름이 있으면 줄 바꿈없이 파일 이름을 바꿔야합니다. 루프가 올바르게 작동하려면 파일 이름을 큰 따옴표로 묶어야합니다.

GNU find및 GNU xargs(또는 FreeBSD (* BSD?) 또는 Mac OS X)가있는 경우 다음 -print0과 같이 및 -0옵션을 사용할 수 있습니다 .

find . -name 'Lemon*.mp3' -print0 | xargs -0 mplayer

이름의 내용에 관계없이 작동합니다 (파일 이름에 표시 할 수없는 두 문자 만 슬래시와 NUL이며 슬래시는 파일 경로에 문제를 일으키지 않으므로 이름 구분 기호로 NUL을 사용하면 모든 것을 다룹니다). 그러나 처음 6 개 항목을 필터링 해야하는 경우 줄 바꿈 대신 NUL로 끝나는 '줄'을 처리하는 프로그램이 필요합니다 ... 그리고 확실하지 않습니다.

첫 번째는 현재 진행중인 특정 사례에서 가장 단순합니다. 그러나 아직 나열하지 않은 다른 시나리오는 다루지 않을 수 있습니다.


2

나는 xargs질문에 직접 대답 하지는 않지만 find-exec옵션을 언급 할 가치가 있음을 알고 있습니다.

다음과 같은 파일 시스템이 제공됩니다.

[root@localhost bokeh]# tree --charset assci bands
bands
|-- Dream\ Theater
|-- King's\ X
|-- Megadeth
`-- Rush

0 directories, 4 files

find 명령은 Dream Theater와 King 's X의 공간을 처리하도록 만들 수 있습니다. 따라서 grep을 사용하여 각 밴드의 드러머를 찾으십시오.

[root@localhost]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King's X:Drums:Jerry Gaskill
bands/Megadeth:Drums:Dirk Verbeuren

에서 -exec옵션{} 경로를 포함한 파일 이름을 의미합니다. 이스케이프하거나 따옴표로 묶을 필요는 없습니다.

-exec종료 자 +와 ( \;) 의 차이점은 +하나의 명령 줄에 가능한 많은 파일 이름을 그룹화한다는 것입니다. 이므로\; 각 파일 이름에 대한 명령을 실행합니다.

따라서 find bands/ -type f -exec grep Drums {} +결과는 다음과 같습니다.

grep Drums "bands/Dream Theater" "bands/Rush" "bands/King's X" "bands/Megadeth"

find bands/ -type f -exec grep Drums {} \;결과는 다음 과 같습니다.

grep Drums "bands/Dream Theater"
grep Drums "bands/Rush"
grep Drums "bands/King's X"
grep Drums "bands/Megadeth"

이 경우 grep파일 이름 인쇄 여부에 따른 부작용이 있습니다.

[root@localhost bokeh]# find bands/ -type f -exec grep Drums {} \;
Drums:Mike Mangini
Drums: Neil Peart
Drums:Jerry Gaskill
Drums:Dirk Verbeuren

[root@localhost bokeh]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King's X:Drums:Jerry Gaskill
bands/Megadeth:Drums:Dirk Verbeuren

물론, grep'옵션을이야 -h-H파일 이름에 관계없이 어떻게 인쇄할지 여부를 제어합니다 grep라고합니다.


xargs

xargs 명령 행에서 man 파일이있는 방법을 제어 할 수도 있습니다.

xargs기본적으로 모든 인수는 한 줄로 그룹화됩니다. -exec \;사용 하는 것과 동일한 작업을 수행합니다 xargs -l. 이 -t옵션은 xargs명령을 실행하기 전에 인쇄하도록 지시 합니다.

[root@localhost bokeh]# find ./bands -type f  | xargs -d '\n' -l -t grep Drums
grep Drums ./bands/Dream Theater 
Drums:Mike Mangini
grep Drums ./bands/Rush 
Drums: Neil Peart
grep Drums ./bands/King's X 
Drums:Jerry Gaskill
grep Drums ./bands/Megadeth 
Drums:Dirk Verbeuren

참조하십시오 -l 옵션은 xargs가 모든 파일 이름에 대해 grep을 실행하도록 지시합니다.

기본값과 비교 ( -l옵션 없음 ) :

[root@localhost bokeh]# find ./bands -type f  | xargs -d '\n'  -t grep Drums
grep Drums ./bands/Dream Theater ./bands/Rush ./bands/King's X ./bands/Megadeth 
./bands/Dream Theater:Drums:Mike Mangini
./bands/Rush:Drums: Neil Peart
./bands/King's X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren

xargs명령 줄에 몇 개의 파일이 있는지 더 잘 제어 할 수 있습니다. -l옵션 당 명령 당 최대 파일 수를 제공하십시오 .

[root@localhost bokeh]# find ./bands -type f  | xargs -d '\n'  -l2 -t grep Drums
grep Drums ./bands/Dream Theater ./bands/Rush 
./bands/Dream Theater:Drums:Mike Mangini
./bands/Rush:Drums: Neil Peart
grep Drums ./bands/King's X ./bands/Megadeth 
./bands/King's X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren
[root@localhost bokeh]# 

grep때문에 두 개의 파일 이름으로 실행 되었음을 참조하십시오 -l2.


1

이 게시물의 특정 제목을 감안할 때 다음은 내 제안입니다.

ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g'

아이디어는 공백을 '<'와 같은 고유 문자로 변환 한 다음 백 슬래시와 공백을 '\'로 변경하는 것입니다. 그런 다음 다음과 같이 원하는 명령으로 파이프 할 수 있습니다.

ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g' | xargs -L1 GetFileInfo

여기서 핵심은 'tr'및 'sed'명령에 있습니다. '?'와 같이 '<'이외의 문자를 사용할 수 있습니다. 또는 탭 문자.


우회 목적은 무엇입니까 tr? 왜 안돼 ls *.mp3 | sed -n '7!b;s/\([[:space:]]\)/\\\1/g;p'?
tripleee

1
"tr '' '?'"는 "sed"의 필요성을 제거한다는 것을 알았습니다. 단일 "?" 문자는 공백이 아니지만 모든 단일 문자와 일치합니다 (이 경우 공백). .mp3로 끝나는 모든 파일을 처리하려고하기 때문에 다른 것이 될 가능성은 매우 작습니다. "ls | grep ''| tr '' '?' xargs -L1 GetFileInfo "
Dick Guertin

"tab"을 동시에 처리 할 수도 있습니다 : tr '\ t' '??' 둘 다 처리합니다.
Dick Guertin

1

대체 솔루션이 도움이 될 수 있습니다 ...

Perl을 사용하여 줄 끝에 널 문자를 추가 한 다음 -0xargs 의 옵션 을 사용할 수도 있습니다. xargs -d '\ n'(승인 된 답변)과 달리 이것은 OS X를 포함한 모든 곳에서 작동합니다.

예를 들어 공백이나 다른 재미있는 문자가 포함될 수있는 MPEG3 파일을 재귀 적으로 나열 (실행, 이동 등)하려면 다음을 사용하십시오.

find . | grep \.mp3 | perl -ne 'chop; print "$_\0"' | xargs -0  ls

(참고 : 필터링의 경우 "find 's"--name 인수보다 기억하기 쉬운 "| grep"구문을 선호합니다.

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