디렉토리의 모든 파일에서 명령 실행


290

누군가 다음을 수행하기 위해 코드를 제공 할 수 있습니까? 파일 디렉토리가 있다고 가정하십시오. 파일 디렉토리는 모두 프로그램을 통해 실행되어야합니다. 프로그램은 결과를 표준 출력으로 출력합니다. 디렉토리로 이동하여 각 파일에서 명령을 실행하고 하나의 큰 출력 파일로 출력을 연결하는 스크립트가 필요합니다.

예를 들어, 1 파일에서 명령을 실행하려면 다음을 수행하십시오.

$ cmd [option] [filename] > results.out

3
질문에 추가하고 싶습니다. xargs를 사용하여 수행 할 수 있습니까? 예 : ls <directory> | xargs cmd [options] {filenames put in here automatically by xargs} [more arguments] > results.out
Ozair Kafray

2
가능하지만 운전 에 사용하고 싶지 않을ls 것입니다 xargs. cmd유능하게 작성된 글 이라면 아마 간단하게 할 수 있습니다 cmd <wildcard>.
tripleee

답변:


425

다음 bash 코드는 $ file을 명령에 전달합니다. 여기서 $ file은 / dir의 모든 파일을 나타냅니다.

for file in /dir/*
do
  cmd [option] "$file" >> results.out
done

el@defiant ~/foo $ touch foo.txt bar.txt baz.txt
el@defiant ~/foo $ for i in *.txt; do echo "hello $i"; done
hello bar.txt
hello baz.txt
hello foo.txt

23
에 파일이없는 경우 /dir/루프는 여전히 '*'for for 값으로 한 번 실행 $file되므로 바람직하지 않습니다. 이를 피하려면 루프 지속 시간 동안 nullglob를 활성화하십시오. 루프 전에이 줄을 추가 shopt -s nullglob하고, 루프 후이 줄을 shopt -u nullglob #revert nullglob back to it's normal default state.
Ste-au

43
+1, 그리고 그것은 내 전체 벽지 컬렉션 비용이 들었습니다. 내 뒤에는 모두 큰 따옴표를 사용하십시오. "$ file"
Behrooz

출력 파일이 루프 내에서 동일하면 루프 외부로 리디렉션하는 것이 훨씬 효율적입니다 done >results.out(여기서 내가 가정 한 것처럼 추가 대신 덮어 쓸 수 있음).
tripleee

입력 파일에 사용자 정의 된 개별 결과 파일을 어떻게 얻습니까?
티모시 스완

1
dir의 많은 파일에이 명령을 사용하여주의하십시오. 대신 find -exec를 사용하십시오.
kolisko

182

이건 어때요:

find /some/directory -maxdepth 1 -type f -exec cmd option {} \; > results.out
  • -maxdepth 1argument는 find가 서브 디렉토리로 재귀 적으로 내려가는 것을 방지합니다. (이러한 중첩 디렉토리를 처리하려면이를 생략 할 수 있습니다.)
  • -type -f 일반 파일 만 처리되도록 지정합니다.
  • -exec cmd option {}찾은 각 파일에 대해 cmd지정된 option파일 이름으로 대체 하여 실행 하도록 지시합니다.{}
  • \; 명령의 끝을 나타냅니다.
  • 마지막으로 모든 개별 cmd실행 의 출력 이 results.out

그러나 파일이 처리되는 순서에 관심이 있다면 루프를 작성하는 것이 좋습니다. 나는 find파일을 inode 순서로 처리 한다고 생각 하지만 (내가 틀릴 수도 있지만) 원하는 것이 아닐 수도 있습니다.


1
이것이 파일을 처리하는 올바른 방법입니다. 여러 가지 이유로 for 루프를 사용하면 오류가 발생하기 쉽습니다. 또한 정렬 기준이 무엇인지에 따라 달라지는 statand와 같은 다른 명령을 사용하여 정렬을 수행 할 수도 있습니다 sort.
tuxdna

1
두 개의 명령을 실행하려면 -exec옵션 후에 어떻게 연결 합니까? 작은 따옴표 나 다른 것으로 포장해야합니까?
frei

find옵션을 사용하여 파일 이름 패턴으로 필터링 할 수 있고 항상 -name단일 명령으로 수행 할 수 있는 최상의 옵션 입니다.
João Pimentel Ferreira

3
@frei 귀하의 질문에 대한 답변은 여기에 있습니다 : stackoverflow.com/a/6043896/1243247 그러나 기본적으로 -exec옵션을 추가하십시오 :find . -name "*.txt" -exec echo {} \; -exec grep banana {} \;
João Pimentel Ferreira

2
파일 이름을 옵션으로 어떻게 참조 할 수 있습니까?
Toskan April

54

나는 이것을 실행하여 명령 줄에서 라즈베리 파이에서 이것을하고있다 :

for i in *;do omxplayer "$i";done

7

받아 들여 지거나 투표율이 높은 답변은 훌륭하지만 몇 가지 중요한 세부 정보가 부족합니다. 이 글은 쉘 경로 이름 확장 (글로브)이 실패 할 때, 파일 이름에 개행 / 대시 기호가 포함되어 있고 결과를 파일.

디렉토리를 사용하여 쉘 글로브 확장을 실행할 때 디렉토리에 파일 *없고 확장되지 않은 글로브 문자열이 실행될 명령으로 전달되어 바람직하지 않은 결과가 발생할 수있는 경우 확장에 실패 할 수 있습니다. bash쉘이 사용하기위한 확장 된 쉘 옵션을 제공합니다 nullglob. 따라서 루프는 기본적으로 파일이 들어있는 디렉토리 내부에서 다음과 같이됩니다

 shopt -s nullglob

 for file in ./*; do
     cmdToRun [option] -- "$file"
 done

이렇게하면 표현식 ./*이 파일을 반환하지 않을 때 (루프 가 비어있는 경우) for 루프를 안전하게 종료 할 수 있습니다.

또는 POSIX 호환 방법 ( nullglobbash특정)

 for file in ./*; do
     [ -f "$file" ] || continue
     cmdToRun [option] -- "$file"
 done

이를 통해 표현식이 한 번 실패하고 [ -f "$file" ]확장되지 않은 문자열 ./*이 해당 디렉토리에서 유효한 파일 이름 인지 여부를 확인하면 루프 내부로 이동할 수 있습니다. 따라서이 조건이 실패 continue하면 for루프를 다시 사용 하여 루프 가 다시 실행되지 않습니다.

또한 --파일 이름 인수를 전달하기 직전에 사용법에 유의하십시오 . 앞에서 언급했듯이 셸 파일 이름은 파일 이름의 아무 곳에 나 대시를 포함 할 수 있기 때문에 필요합니다. 일부 쉘 명령은이를 해석하여 이름이 올바르게 인용되지 않은 경우이를 명령 옵션으로 취급하고 플래그가 제공되는지 생각하는 명령을 실행합니다.

--경우 명령 줄 옵션의 끝을 알립니다. 즉, 명령은이 시점 이후의 문자열을 명령 플래그로 해석하지 말고 파일 이름으로 만 해석해야합니다.


파일 이름을 큰 따옴표로 묶으면 이름에 glob 문자 나 공백이 포함 된 경우가 해결됩니다. 그러나 * nix 파일 이름에는 개행 문자도 포함될 수 있습니다. 따라서 유효한 파일 이름의 일부가 될 수없는 유일한 문자 (널 바이트 ( \0))로 파일 이름을 구분합니다 . 이후 bash내부적으로 사용하는 C널 바이트 문자열의 끝을 표시하는 데 사용되는 스타일 문자열을,이에 적합한 후보입니다.

따라서 printf쉘 옵션을 사용하여 명령 -d옵션을 사용 하여이 NULL 바이트로 파일을 구분하면 다음과 같이 read할 수 있습니다

( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
    cmdToRun [option] -- "$file"
done

nullglob과는 printf감싸된다 (..)방지하기 때문에, 그들은 기본적으로 서브 쉘 (자식 쉘)에서 실행되는 방법을 nullglob명령이 종료되면, 부모 쉘에 반영하기 위해 옵션을 선택합니다. -d ''의 옵션 read명령은 하지 그래서 필요 POSIX 호환 bash이 수행하는 쉘. find명령을 사용하면 다음과 같이 할 수 있습니다

while IFS= read -r -d '' file; do
    cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0)

내용은 find지원하지 않습니다 구현 -print0합니다 (GNU와 FreeBSD의 구현 제외), 이것은 사용하여 에뮬레이션 할 수 있습니다printf

find . -maxdepth 1 -type f -exec printf '%s\0' {} \; | xargs -0 cmdToRun [option] --

또 다른 중요한 수정은 많은 수의 파일 I / O를 줄이기 위해 리디렉션을 for 루프 밖으로 이동시키는 것입니다. 루프 내에서 사용될 때, 쉘은 for 루프의 각 반복에 대해 시스템 호출을 두 번 실행해야합니다. 한 번은 파일과 연관된 파일 설명자를 열고 한 번은 닫습니다. 큰 반복을 실행하면 성능에 병목 현상이 발생합니다. 루프 외부로 옮기는 것이 좋습니다.

이 수정으로 위의 코드를 확장하면 할 수 있습니다

( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
    cmdToRun [option] -- "$file"
done > results.out

기본적으로 파일 입력의 각 반복에 대한 명령 내용을 stdout에 넣고 루프가 끝나면 대상 파일을 한 번 열어 stdout의 내용을 쓰고 저장하십시오. 동일한 find버전은

while IFS= read -r -d '' file; do
    cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0) > results.out

1
파일이 존재하는지 확인하기 위해 +1 기존 디렉토리가 아닌 디렉토리에서 검색하는 경우 $ file에 유효한 파일 이름이 아닌 정규식 문자열 "/ invald_dir / *"이 포함됩니다.
cdalxndr

3

때로는 작업을 수행하는 빠르고 더러운 방법 중 하나가 있습니다.

find directory/ | xargs  Command 

예를 들어 현재 디렉토리의 모든 파일에서 여러 줄을 찾으려면 다음을 수행하십시오.

find . | xargs wc -l

8
@Hubert 왜 파일 이름에 줄 바꿈이 있습니까?!
musicin3d

2
파일 이름은 인쇄 가능한 문자를 포함 할 필요가 없으며 유효한 UTF-8 시퀀스 일 필요도 없습니다. 또한, 개행 문자는 인코딩에 크게 의존하며, 하나의 인코딩 ♀는 다른 개행 문자입니다. 코드 페이지 437 참조
Hubert Kario

2
정말요? 이것은 99.9 %의 시간 동안 작동하며, "빠르고 더럽다"고 말함
Edoardo

나는 "빠르고 더러운"(일명 "깨진") 배쉬 스크립트의 팬이 아닙니다. 조만간 유명한 "Moved ~/.local/share/steam. Ran steam. 그것은 사용자가 소유 한 시스템의 모든 것을 삭제했습니다. "와 같은 것으로 끝납니다 . 버그 보고서.
활동 감소

이름에 공백이있는 파일에서는 작동하지 않습니다.
Shamas S-복원 모니카

2

한 디렉토리에서 다른 디렉토리로 모든 .md 파일을 복사해야 했으므로 여기에 내가 한 일이 있습니다.

for i in **/*.md;do mkdir -p ../docs/"$i" && rm -r ../docs/"$i" && cp "$i" "../docs/$i" && echo "$i -> ../docs/$i"; done

어느 것이 읽기 어렵 기 때문에 분석해 봅시다.

먼저 파일과 함께 디렉토리에 들어갑니다.

for i in **/*.md; 패턴의 각 파일마다

mkdir -p ../docs/"$i"파일을 포함하는 폴더 외부의 문서 폴더에 해당 디렉토리를 만드십시오. 해당 파일과 이름이 같은 추가 폴더를 만듭니다.

rm -r ../docs/"$i" 결과로 생성 된 추가 폴더를 제거합니다 mkdir -p

cp "$i" "../docs/$i" 실제 파일을 복사

echo "$i -> ../docs/$i" 당신이 한 일을 에코

; done 그 이후 행복하게 살았습니다


참고 :에 대한 **하려면 globstar쉘 옵션을 설정해야합니다 :shopt -s globstar
휴 버트 Kario

2

당신이 사용할 수있는 xarg

ls | xargs -L 1 -d '\n' your-desired-command

-L 1 한 번에 1 개의 항목을 전달합니다

-d '\n'ls줄 바꿈에 따라 출력 이 분할됩니다.


1

@Jim Lewis의 접근 방식을 기반으로합니다.

다음은 find수정 날짜별로 파일을 사용 하고 정렬 하는 빠른 솔루션입니다 .

$ find  directory/ -maxdepth 1 -type f -print0 | \
  xargs -r0 stat -c "%y %n" | \
  sort | cut -d' ' -f4- | \
  xargs -d "\n" -I{} cmd -op1 {} 

정렬은 다음을 참조하십시오.

http://www.commandlinefu.com/commands/view/5720/find-files-and-list-them-sorted-by-modification-time


파일이 이름에 줄 바꿈이있는 경우이 작동하지 않습니다
휴 버트 Kario에게

1
당신은 더 읽은 할 수 있습니다 @HubertKario -print0에 대한 find-0xargs있는 대신 (줄 바꿈 포함) 공백의 널 문자를 사용합니다.
tuxdna

예, 사용하는 -print0것이 도움이되지만 전체 파이프 라인은 이와 같은 것을 사용해야하지만 sort그렇지 않습니다.
Hubert Kario

1

간단한 해결책은 다음과 같습니다.

sh /dir/* > ./result.txt

2
질문을 올바르게 이해 했습니까? 이것은 마치 스크립트 인 것처럼 쉘을 통해 디렉토리의 각 파일을 실행하려고 시도합니다.
rdas

1

막심

Jim Lewis의 대답 과 잘 작동한다는 것을 알았습니다 .

$ export DIR=/path/dir && cd $DIR && chmod -R +x *
$ find . -maxdepth 1 -type f -name '*.sh' -exec {} \; > results.out

정렬 순서

정렬 순서대로 실행하려면 다음과 같이 수정하십시오.

$ export DIR=/path/dir && cd $DIR && chmod -R +x *
find . -maxdepth 2 -type f -name '*.sh' | sort | bash > results.out

예를 들어, 다음 순서로 실행됩니다.

bash: 1: ./assets/main.sh
bash: 2: ./builder/clean.sh
bash: 3: ./builder/concept/compose.sh
bash: 4: ./builder/concept/market.sh
bash: 5: ./builder/concept/services.sh
bash: 6: ./builder/curl.sh
bash: 7: ./builder/identity.sh
bash: 8: ./concept/compose.sh
bash: 9: ./concept/market.sh
bash: 10: ./concept/services.sh
bash: 11: ./product/compose.sh
bash: 12: ./product/market.sh
bash: 13: ./product/services.sh
bash: 14: ./xferlog.sh

무제한 깊이

특정 조건에 따라 무제한 깊이로 실행하려면 다음을 사용할 수 있습니다.

export DIR=/path/dir && cd $DIR && chmod -R +x *
find . -type f -name '*.sh' | sort | bash > results.out

그런 다음 자식 디렉토리의 각 파일 위에 다음과 같이 넣으십시오.

#!/bin/bash
[[ "$(dirname `pwd`)" == $DIR ]] && echo "Executing `realpath $0`.." || return

부모 파일의 본문 어딘가에 :

if <a condition is matched>
then
    #execute child files
    export DIR=`pwd`
fi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.