xargs란?
xargs
는 표준 입력(stdin) 으로부터 값을 받아서, 명령어의 인자(argument) 로 넘겨주는 역할을 합니다.
즉, 어떤 명령어가 출력한 리스트를 받아서, 이를 또 다른 명령어에 넘겨주는 중간 역할을 합니다.
기본 구조
command1 | xargs command2
command1
의 출력 결과를xargs
가 받아서command2
에 인자로 넘겨 실행
xargs를 쓰는 이유?
xargs
는 파이프(|
)로 전달받은 표준 입력(stdin)을 커맨드 라인 인자(argument)로 변환해 주는 역할을 합니다.
예를 들어, find
로 찾은 파일 목록을 rm
으로 삭제하는 것처럼요.
# 현재 디렉터리의 모든 .log 파일을 찾아 삭제한다
find . -name "*.log" | xargs rm
xargs의 두 가지 모드
xargs
에는 두 가지 모드가 존재합니다.
기본 추가 모드 와 대체 모드 (-I {}
) 가 있죠.
기본 모드
-I
옵션 없이 xargs
를 사용할 때의 기본 동작 방식입니다.
xargs는 최대한 효율적으로 동작하는 것을 목표로 합니다. 입력을 하나씩 처리하는 대신, 가능한 많은 입력값(파일 목록 등)을 한 번에 모아서 명령어의 맨 끝에 인자로 추가합니다.
- 🏃♂️ 비유: 쇼핑 목록(입력값)에 있는 물건들을 카트 하나(명령어)에 전부 담아서 한 번에 계산하는 것과 같습니다.
예시
위에서 봤던 로그 파일 삭제 예시를 다시 볼까요?
만약 a.log
, b.log
, c.log
파일이 발견되었다면, xargs는 다음과 같이 단 한 줄의 명령어를 만들어 실행합니다.
# xargs가 최종적으로 실행하는 명령어
rm a.log b.log c.log
rm a.log
, rm b.log
... 처럼 여러 번 실행하는 것보다 훨씬 빠르고 효율적입니다.
언제 사용할까?
rm
, cp
, docker stop
, cat처럼
여러 개의 인자를 한 번에 받아 처리할 수 있는 명령어와 함께 사용할 때 가장 이상적입니다.
대체 모드 (-I
)
-I {}
옵션을 사용할 때 xargs
는 완전히 다르게 동작합니다.
xargs는 입력값을 한 줄(항목)씩 읽어 들여, 지정된 **대체 문자열({})**을 해당 입력값으로 바꾼 후 명령어를 실행합니다.
입력된 줄의 수만큼 명령어가 반복 실행됩니다. {}
는 관례적으로 많이 사용하며, xargs -I R
처럼 다른 문자를 사용해도 됩니다.
- 👨🍳 비유: 레시피(입력값)에 있는 요리들을 하나씩 순서대로, 정해진 조리법(명령어)에 따라 요리하는 것과 같습니다.
예시
모든 .txt
파일의 이름 앞에 old_ 라는 접두사를 붙여서 이동(rename)시키는 작업을 해봅시다.
ls *.txt | xargs -I {} mv {} old_{}
만약 file1.txt
, file2.txt
가 있다면, xargs는 다음과 같이 여러 개의 명령어를 순차적으로 실행합니다.
# 첫 번째 입력값(file1.txt)으로 실행
mv file1.txt old_file1.txt
# 두 번째 입력값(file2.txt)으로 실행
mv file2.txt old_file2.txt
이런 작업은 mv file1.txt file2.txt old_file1.txt old_file2.txt
처럼 한 번에 처리할 수 없으므로, -I {}
모드가 반드시 필요합니다.
언제 사용할까?
- 입력값을 명령어의 중간에 넣어야 할 때
- 하나의 입력값에 대해 여러 번의 조작이 필요할 때 (... {} ... {} ...)
- 실행할 명령어가 한 번에 하나의 인자만 처리할 수 있을 때
✨ 보너스 팁: 스크립트를 견고하게 만드는 -r 옵션
xargs를 스크립트에서 사용할 때, 파이프로 전달되는 입력이 비어있는 경우도 생길 수 있습니다.
이때 -r
(또는 --no-run-if-empty
) 옵션이 없다면 xargs는 인자 없이 명령어를 실행하려다 오류를 냅니다.
xargs -r은 입력이 비어있으면 아예 명령어를 실행하지 않도록 막아주는 아주 중요한 안전장치입니다.
# 습관처럼 -r을 붙여주면 스크립트가 훨씬 안전해집니다.
find . -name "*.bak" | xargs -r rm
자주 사용하는 xargs 옵션
옵션 | 설명 |
---|---|
-n N |
한 번에 넘길 인자 수 제한 |
-0 |
null 문자(\0 ) 기준 인자 처리 (-print0 과 함께 사용) |
-I {} |
자리표시자 사용 (반복된 위치에 사용 가능) |
-P N |
병렬 프로세스 수 |
-d DELIM |
사용자 지정 구분자 사용 |
실전 예제 모음
1. 텍스트 파일을 찾아서 내용 출력하기
find . -name "*.txt" | xargs cat
- 현재 디렉토리 이하에서
.txt
파일을 모두 찾아 cat
명령어로 내용을 출력합니다.
2. 특정 문자열이 포함된 파일 찾기
find . -type f | xargs grep "에러"
- 현재 디렉토리 이하에서 "에러"라는 문자열이 포함된 파일 내용을 출력합니다.
3. 파일 이름에 공백이 있을 때 (안전하게)
find . -type f -name "*.txt" -print0 | xargs -0 grep "오류"
-print0
: 널 문자(\0
)로 구분해서 출력-0
:xargs
가 널 문자 기준으로 인자 구분
파일 이름에 공백, 탭, 개행 등이 있어도 안전하게 처리할 수 있습니다.
4. 자리표시자 사용하기
echo "file1.txt file2.txt" | xargs -n 1 -I {} echo "처리 중: {}"
-n 1
: 한 번에 하나의 인자 전달-I {}
: 자리표시자로{}
사용
결과:
처리 중: file1.txt
처리 중: file2.txt
5. 병렬 처리로 속도 높이기
cat urls.txt | xargs -n 1 -P 4 curl -O
urls.txt
에 있는 파일들을 4개 병렬로 다운로드-P 4
: 4개의 프로세스 병렬 실행-n 1
: 한 줄씩 하나씩 넘김
xargs vs -exec
find
명령어에서 -exec
옵션을 사용해도 동일한 동작을 할 수 있다는거 알고 계시죠?
하지만 성능과 유연성 측면에서 xargs
가 더 유리할 때가 많아요.
# -exec 사용
find . -name "*.txt" -exec cat {} \;
# xargs 사용
find . -name "*.txt" | xargs cat
-exec
: 파일마다 명령 실행 -> 느림xargs
: 여러 인자를 한 번에 처리 -> 빠름