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

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

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

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



답변

다음 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


답변

이건 어때요:

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 순서로 처리 한다고 생각 하지만 (내가 틀릴 수도 있지만) 원하는 것이 아닐 수도 있습니다.


답변

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

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


답변

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

디렉토리를 사용하여 쉘 글로브 확장을 실행할 때 디렉토리에 파일 *없고 확장되지 않은 글로브 문자열이 실행될 명령으로 전달되어 바람직하지 않은 결과가 발생할 수있는 경우 확장에 실패 할 수 있습니다. 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


답변

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

find directory/ | xargs  Command 

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

find . | xargs wc -l


답변

한 디렉토리에서 다른 디렉토리로 모든 .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 그 이후 행복하게 살았습니다


답변

당신이 사용할 수있는 xarg


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

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

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