많은 수의 파일을 올바른 순서로 묶습니다.


23

내가 명명 된 15,000에 대한 파일이 file_1.pdb, file_2.pdb, 수행하여 순서대로 몇 천에 대한 등 I CAN 고양이 :

cat file_{1..2000}.pdb >> file_all.pdb

그러나 15,000 파일에 대해이 작업을 수행하면 오류가 발생합니다.

-bash: /bin/cat: Argument list too long

나는이 문제가 해결함으로써 해결되는 것을 find . -name xx -exec xx보았지만 파일이 결합 된 순서를 유지하지는 못합니다. 어떻게하면 되나요?


3
열 번째 파일은 무엇입니까? (또는 한 자리 이상의 숫자로 된 파일을 가진 파일도 있습니다.)
roaima

나는 (현재) 디렉토리에 15,000 개의 파일을 가지고 있으며 당신의 cat file_{1..15000}.pdb구조는 나에게 잘 작동합니다.
roaima

11
한계가 무엇인지 시스템에 따라 다릅니다. getconf ARG_MAX말해야한다.
ilkkachu

3
질문을 "수천"또는 "매우 많은"파일로 변경하십시오. 비슷한 문제를 가진 다른 사람들에게 질문을 쉽게 찾을 수 있습니다.
msouth

답변:


49

사용 find, sortxargs:

find . -maxdepth 1 -type f -name 'file_*.pdb' -print0 |
sort -zV |
xargs -0 cat >all.pdb

find명령은 모든 관련 파일을 찾은 다음 경로 이름을 sort"버전 정렬"로 인쇄 하여 올바른 순서로 가져옵니다 (파일 이름의 숫자가 고정 너비로 ​​채워지지 않은 경우에는 필요하지 않습니다 -V). xargs이 정렬 된 경로 이름 목록을 가져 와서 cat가능한 한 큰 배치로 실행합니다.

파일 이름에 줄 바꿈 및 공백과 같은 이상한 문자가 포함되어 있어도 작동합니다. 우리는 정렬로 null로 끝나는 이름 을 부여하기 위해 -print0with find를 사용 하고 를 사용하여 처리합니다 . 또한 플래그로 널 종료 이름을 읽습니다 .sortsort-zxargs-0

이름이 pattern과 일치하지 않는 파일에 결과를 쓰고 있습니다 file_*.pdb.


위의 솔루션은 일부 유틸리티에 비표준 플래그를 사용합니다. 이것들은 이러한 유틸리티의 GNU 구현과 최소한 OpenBSD 및 macOS 구현에 의해 지원됩니다.

사용 된 비표준 플래그는

  • -maxdepth 1, 만들 find에만 최상위 디렉토리하지만 하위 디렉토리를 입력합니다. POSIX로 사용find . ! -name . -prune ...
  • -print0도 걸 find(POSIX이 고려되지만 거부) 출력 NUL 종료 경로 이름. -exec printf '%s\0' {} +대신 사용할 수 있습니다 .
  • -z, 만들려면 sort걸릴 NUL 종료 기록을. POSIX 동등성은 없습니다.
  • -V, 만드는 sort종류의 예 2003. POSIX 동등성은 없지만 파일 이름에 고정 접두사가 있으면 파일 이름의 특정 부분에서 숫자 정렬로 대체 될 수 있습니다.
  • -0, 만들려면 xargs읽기 NUL 종료 기록을. POSIX 동등성은 없습니다. POSIX에서는 파일 이름을로 인식되는 형식으로 인용해야합니다 xargs.

경로명이 올바르게 작동하고 디렉토리 구조가 평탄한 경우 (하위 디렉토리 없음)는 -Vwith를 제외하고 이러한 플래그없이 수행 할 수 sort있습니다.


1
이를 위해 비표준 null 종료가 필요하지 않습니다. 이러한 파일 이름은 지루하고 POSIX 도구는 완전히 처리 할 수 ​​있습니다.
케빈

6
또한 asker의 사양을로 printf ‘file_%d.pdb\0’ {1..15000} | xargs -0 cat또는 심지어 Kevin의 요점 으로 간결하게 작성할 수도 있습니다 echo file_{1..15000}.pdb | xargs cat. 이 find솔루션은 파일 시스템에서 해당 파일을 검색해야하기 때문에 오버 헤드가 상당히 크지 만 일부 파일이 없을 경우 더 유용합니다.
kojiro

4
@ 케빈 (Kevin) 당신이 말하는 것은 사실이지만, 더 일반적인 상황에 적용되는 대답을하는 것이 더 낫습니다. 이 질문을 가진 다음 천명의 사람들 중 일부는 공백이나 파일 이름에 무엇이든 가질 것입니다.
msouth

1
@chrylis 재 지정은 명령의 인수의 일부 적이 없으며, 그건 xargs보다는 cat그가 (각 리디렉션 cat호출이 사용하는 xargs표준 출력). 우리가 말했다 xargs -0 sh -c 'cat >all.pdb'면 , >>대신에 사용하는 것이 합리적 >일 것입니다.
Kusalananda

1
sort -n -k1.6작동하는 것처럼 보입니다 (원본, file_nnn파일 이름 또는 sort -n -k1.5밑줄이없는 파일의 경우 ).
Scott

14

zsh(이 경우 {1..15000}운영자가에서 온다)

autoload zargs # best in ~/.zshrc
zargs file_{1..15000}.pdb -- cat > file_all.pdb

또는 모든 file_<digits>.pdb파일의 번호 순서 :

zargs file_<->.pdb(n) -- cat > file_all.pdb

(여기서 <x-y>진수에 일치 X, Y에있는 글로브 연산자 아니오.하지 x않고 y는 임의의 십진수이다. 상당 extendedglob[0-9]##또는 kshglob+([0-9])(하나 개 이상의 숫자)).

ksh93, 그 내장을 사용하여 cat명령을 (그래서의 한계에 의해 영향을받지 execve()아니가 없기 때문에 시스템 호출 실행 ) :

command /opt/ast/bin/cat file_{1..15000}.pdb > file_all.pdb

bash/ zsh/ ksh93(지원되는 zsh{x..y}와 가진 printf내장) :

printf '%s\n' file_{1..15000}.pdb | xargs cat > file_all.pdb

GNU 시스템 또는 호환 가능한 경우 다음을 사용할 수도 있습니다 seq.

seq -f 'file_%.17g.pdb' 15000 | xargs cat > file_all.pdb

를 들어 xargs기반 솔루션, 특별 관리는 공백, 작은 따옴표 나 큰 따옴표 또는 백 슬래시를 포함하는 파일 이름을주의해야합니다.

를 위해 다음 -It's a trickier filename - 12.pdb을 사용하십시오.

seq -f "\"./-It's a trickier filename - %.17g.pdb\"" 15000 |
  xargs cat > file_all.pdb

seq -f | xarg cat > 가장 우아하고 효과적인 솔루션입니다. (IMHO).
Hastur

까다로운 파일 이름을 확인하십시오. 아마도 '"./-It'\''s a trickier filename - %.17g.pdb"'?
Hastur

@Hastur, 죄송합니다! 예, 감사합니다. 다른 인용 구문으로 변경했습니다. 당신도 잘 작동합니다.
Stéphane Chazelas

11

for 루프가 가능하며 매우 간단합니다.

for i in file_{1..15000}.pdb; do cat $i >> file_all.pdb; done

단점은 당신 cat이 지옥을 여러 번 불러내 는 것입니다. 그러나 작업을 수행하는 방법을 정확하게 기억할 수없고 find상황에서 호출 오버 헤드가 그렇게 나쁘지 않은 경우 염두에 두어야합니다.


나는 종종 echo $i;루프 본문에 "진행률 표시기"를 추가합니다
Rolf

3
seq 1 15000 | awk '{print "file_"$0".dat"}' | xargs cat > file_all.pdb

1
awk는 여기서 seq의 작업을 수행 할 수 있고 seq는 awk의 작업을 수행 할 수 있습니다 seq -f file_%.10g.pdb 15000. 참고 seq표준 명령이 아닙니다.
Stéphane Chazelas

고마워 Stéphane-나는 이것을 seq -f 하는 좋은 방법 이라고 생각 합니다; 기억할 것입니다.
LarryC

2

전제

특정 이름 형식이 [ 1 , 2 ] 인 15k 파일 에만 해당 오류가 발생하지 않아야합니다 .

다른 디렉토리에서 해당 확장을 실행 중이고 각 파일에 경로를 추가해야하는 경우 명령의 크기가 커지고 물론 발생할 수 있습니다.

솔루션 은 해당 디렉토리에서 명령을 실행하십시오.

(cd That/Directory ; cat file_{1..2000}.pdb >> file_all.pdb )

최상의 해결책 대신 내가 잘못 추측하고 파일이있는 디렉토리에서 실행하면 ...
IMHO 최고의 해결책은 Stéphane Chazelas의 것입니다 .

seq -f 'file_%.17g.pdb' 15000 | xargs cat > file_all.pdb

printf 또는 seq와 함께; 미리 캐시 된 내부의 번호 만 사용하여 15k 파일에서 테스트 된 경우 더 빠릅니다 (현재는 파일이있는 동일한 디렉토리의 OP 파일 제외).

더 많은 단어

쉘 명령 행에 더 오래 전달할 수 있어야합니다.
명령 행은 213914 자이며 15003 단어를 포함합니다
cat file_{1..15000}.pdb " > file_all.pdb" | wc

... 각 단어에 8 바이트를 추가해도 ARG_MAX커널 3.13.0에서 보고 한 2097142 (2.1M)에서 333 938 바이트 (0.3M) 나 "실제로 명령 할 수있는 최대 명령 길이" 사용 " 으로xargs --show-limits

시스템의 출력을 살펴보십시오.

getconf ARG_MAX
xargs --show-limits

게으름 유도 솔루션

이 경우 일반적으로 시간 효율적인 솔루션이 나오기 때문에 블록으로 작업하는 것을 선호합니다.
논리 (있는 경우)는 1 ... 1000 1001..2000 등을 작성하기에는 너무 게으르다 ...
그래서 스크립트를 작성 해달라고 부탁한다.
출력이 올바른지 확인한 후에 만 ​​스크립트로 리디렉션합니다.

...하지만 게으름은 마음의 상태입니다 .
나는 알레르기가 있고 xargs(실제로 xargs여기에 사용해야 했음) 사용 방법을 확인하고 싶지 않기 때문에 아래 예제 (tl; dr)에서와 같이 바퀴를 재발 명하기 위해 엄밀히 마무리합니다.

파일 이름이 제어되므로 (공백, 줄 바꾸기 없음) 아래 스크립트와 같은 방법으로 쉽게 이동할 수 있습니다.

tl; dr

버전 1 : 첫 번째 파일 번호, 마지막, 블록 크기, 출력 파일을 선택적 매개 변수로 전달

#!/bin/bash
StartN=${1:-1}          # First file number
EndN=${2:-15000}        # Last file number
BlockN=${3:-100}        # files in a Block 
OutFile=${4:-"all.pdb"} # Output file name

CurrentStart=$StartN 
for i in $(seq $StartN $BlockN $EndN)
do 
  CurrentEnd=$i ;  
    cat $(seq -f file_%.17g.pdb $CurrentStart $CurrentEnd)  >> $OutFile;
  CurrentStart=$(( CurrentEnd + 1 )) 
done
# Here you may need to do a last iteration for the part cut from seq
[[ $EndN -ge $CurrentStart ]] && 
    cat $(seq -f file_%.17g.pdb $CurrentStart $EndN)  >> $OutFile;

버전 2

확장을 위해 bash 호출 (내 테스트에서 ~ 20 % 조금 느려짐).

#!/bin/bash
StartN=${1:-1}          # First file number
EndN=${2:-15000}        # Last file number
BlockN=${3:-100}        # files in a Block 
OutFile=${4:-"all.pdb"} # Output file name

CurrentStart=$StartN 
for i in $(seq $StartN $BlockN $EndN)
do 
  CurrentEnd=$i ;
    echo  cat file_{$CurrentStart..$CurrentEnd}.pdb | /bin/bash  >> $OutFile;
  CurrentStart=$(( CurrentEnd + 1 )) 
done
# Here you may need to do a last iteration for the part cut from seq
[[ $EndN -ge $CurrentStart ]] && 
    echo  cat file_{$CurrentStart..$EndN}.pdb | /bin/bash  >> $OutFile;

물론 앞으로 나아 와서 seq [ 3 ] (coreutils에서)를 완전히 없애고 bash의 변수로 직접 작업하거나 python을 사용하거나 ac 프로그램을 컴파일하여 수행 할 수 있습니다 [ 4 ] ...


%g약자입니다 %.6g. 예를 들어 1,000,000을 1e + 06으로 나타냅니다.
Stéphane Chazelas

정말 게으른 사람은 주위 작업의 작업을 위해 설계된 도구를 사용하여 같은 E2BIG 제한 xargszsh을의, zargs또는 ksh93의가 command -x.
Stéphane Chazelas

seqbash 내장이 아니며 GNU coreutils의 명령입니다. seq -f %g 1000000 1000000최신 버전의 coreutils에서도 1e + 06을 출력합니다.
Stéphane Chazelas

@ StéphaneChazelas Laziness는 마음의 상태입니다. 이상하게 말하지만 직렬화 된 명령의 출력을 시각적으로 확인하고 실행으로 리디렉션 할 때 더 아늑합니다. 그 건설은 저보다 덜 생각하게 xarg하지만 ... 나는 그것이 개인적이고 나에게만 관련이 있다는 것을 이해합니다.
Hastur

@ StéphaneChazelas Gotcha, 맞아 ... 고쳤다. 감사. OP가 제공 한 15k 파일로만 테스트했습니다.
Hastur

0

그것을 할 수있는 또 다른 방법은

(cat file_{1..499}.pdb; cat file_{500..999}.pdb; cat file_{1000..1499}.pdb; cat file_{1500..2000}.pdb) >> file_all.pdb
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.