전제

전제

등의 이름을 가진 파일이 약 15,000개 있습니다 file_1.pdb. file_2.pdb다음을 수행하면 약 수천 개의 파일을 순서대로 정렬할 수 있습니다.

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

하지만 15,000개의 파일로 이 작업을 수행하면 오류가 발생합니다.

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

나는 이것을 수행하여 이 문제가 해결되는 것을 보았지만 find . -name xx -exec xx이것은 파일 연결 순서를 유지하지 않습니다. 이 목표를 어떻게 달성할 수 있나요?

답변1

find, sortxargs:을 사용하십시오 .

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

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

이는 파일 이름에 이상한 문자(예: 개행 및 공백)가 포함된 경우에도 작동합니다. -print0with를 사용하여 findnul sort로 끝나는 이름을 정렬하고 sort이러한 이름을 처리하는 데 사용합니다 -z. xargs또한 해당 플래그를 사용하여 null로 끝나는 이름을 읽습니다 -0.

이름이 패턴과 일치하지 않는 파일에 결과를 쓰고 있다는 점에 유의하세요 file_*.pdb.


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

사용되는 비표준 플래그는 다음과 같습니다.

  • -maxdepth 1, find하위 디렉토리가 아닌 최상위 디렉토리에만 들어갑니다. POSIXly, 사용find . ! -name . -prune ...
  • -print0, find출력이 null로 끝나는 경로 이름이 됩니다(POSIX에서는 고려되지만 거부됨). 대신 사용할 수 있습니다 -exec printf '%s\0' {} +.
  • -z, null로 끝나는 레코드를 가져옵니다 sort. POSIX에 해당하는 것은 없습니다.
  • -V, sort정렬(예 200: 3. POSIX와 동등한 것은 없지만 파일 이름에 고정 접두사가 있는 경우 파일 이름의 특정 부분을 숫자 순서로 대체할 수 있습니다.
  • -0, xargsnull로 끝나는 레코드를 읽습니다. POSIX에 해당하는 것은 없습니다. POSIXly에서는 xargs.

-V경로 이름이 제대로 작동하고 디렉토리 구조가 단순(하위 디렉토리 없음)인 경우 를 제외 하고 이러한 플래그를 사용할 수 없습니다 sort.

답변2

( 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까지 일치하는 전역 연산자입니다. xnor가 없으면 y임의의 십진수입니다. extendedglobs [0-9]##또는 kshglobs +([0-9])(하나 이상의 숫자)와 동일합니다.)

Use 는 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

답변3

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

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

cat단점은 여러 번 호출한다는 것입니다 . 그러나 이러한 작업을 수행하는 방법을 정확히 기억할 수 없고 find호출 오버헤드가 그다지 나쁘지 않다면 기억해 두는 것이 좋습니다.

답변4

전제

이런 실수를 해서는 안 된다오직해당 특정 이름 형식의 파일 15,000개 [1,2] .

다른 디렉터리에서 확장 프로그램을 실행하고 각 파일에 경로를 추가해야 하는 경우 명령의 크기가 더 커집니다. 물론 이런 일이 발생할 수 있습니다.

해결책이 디렉터리에서 명령을 실행합니다.

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

최고의 솔루션대신 내가 잘못 추측해서 파일이 있는 디렉터리에서 실행한다면...
IMHO 가장 좋은 해결책은 다음과 같습니다.스테판 차젤라스의 작품:

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

printf 또는 seq와 함께 사용되며 15k 파일에서 테스트되었으며 파일 수만 사전 캐싱하여 훨씬 더 빨랐습니다(현재 파일과 동일한 디렉터리에 있는 OP 파일 제외).

몇 마디 더 말해 보세요

더 긴 쉘 명령줄을 전달할 수 있어야 합니다.
명령줄 길이는 15003자를 포함하여 213914자입니다.성격
cat file_{1..15000}.pdb " > file_all.pdb" | wc

...워드당 8바이트를 추가해도 333,938바이트(0.3M)가 됩니다. 이는 커널 3.13.0에서 보고된 2097142(2.1M) ARG_MAX또는 약간 더 작은 2088232 보다 훨씬 적습니다."실제로 사용할 수 있는 명령의 최대 길이"통과xargs --show-limits

시스템에서 다음 출력을 봅니다.

getconf ARG_MAX
xargs --show-limits

게으른 부팅 솔루션

이 경우에는 블록을 사용하는 것이 일반적으로 시간 효율적인 솔루션이 되기 때문에 선호합니다.
(있는 경우) 논리는 1...1000 1001..2000 등을 작성하기에는 너무 게으르다는 것입니다.
그래서 스크립트를 요청했습니다.
출력의 정확성을 확인한 후에야 이를 스크립트로 리디렉션합니다.

...하지만 게으름은 마음의 상태입니다.
나는 xargs(여기서 꼭 사용해야 하는 xargs) 알레르기가 있고 어떻게 사용하는지 확인하고 싶지 않았기 때문에 아래 예(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 .] (coreutils에서) bash에서 직접 변수를 사용하거나 Python을 사용하거나 c 프로그램을 컴파일하여 [4] ...

관련 정보